Go Integration Guide
Send logs from your Go application to logging.cheap using the OpenTelemetry SDK and OTLP (OpenTelemetry Protocol).
This guide uses the otelslog bridge
— an adapter that connects Go's standard log/slog to OpenTelemetry.
Your existing slog.Info(), slog.Error(), etc. calls
automatically send logs to logging.cheap with no API changes in your application code.
How it works: Your app → slog → otelslog bridge → OTLP over HTTP → logging.cheap. No agents to install, no collectors to manage, no YAML to configure.
Prerequisites
- Go 1.21 or later (required for log/slog)
- A Go module — run
go mod init example.com/myappif starting a new project - A logging.cheap account
Get an API Key
Create an API key from your Settings page.
Copy the key — it starts with cl_live_ and is only shown once.
If you just signed up, an API key prompt appears on your first login.
Add Dependencies
go get go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp \
go.opentelemetry.io/otel/sdk/log \
go.opentelemetry.io/otel/sdk/resource \
go.opentelemetry.io/contrib/bridges/otelslog
Set Environment Variables
The OpenTelemetry SDK reads its configuration from standard
OTEL_* environment variables — no hardcoded endpoints or secrets in your code.
Set these before running your application:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://logging.cheap"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer cl_live_YOUR_API_KEY"
export OTEL_SERVICE_NAME="your-service-name"
# OpenTelemetry supports logs, traces, and metrics.
# logging.cheap only handles logs, so disable the others
# to avoid unnecessary network requests.
export OTEL_TRACES_EXPORTER="none"
export OTEL_METRICS_EXPORTER="none"
Replace cl_live_YOUR_API_KEY with the key you copied earlier, and
your-service-name with a name that identifies your application in the log viewer
(e.g. api-server, checkout-service).
Make sure to include the Bearer prefix — without it, authentication will fail.
The key=value format is how OTEL_EXPORTER_OTLP_HEADERS encodes HTTP headers — the SDK sends this as a standard Authorization: Bearer ... header.
Initialize OpenTelemetry Logging
Create a main.go file. The OTLP exporter reads configuration
from the environment variables above:
package main
import (
"context"
"fmt"
"log/slog"
"os"
"time"
"go.opentelemetry.io/contrib/bridges/otelslog"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/resource"
)
func main() {
ctx := context.Background()
// Create an OTLP exporter that sends logs to logging.cheap over HTTP.
exporter, err := otlploghttp.New(ctx)
if err != nil {
slog.Error("Failed to create OTLP log exporter", "error", err)
os.Exit(1)
}
// The batch processor buffers logs and sends them in batches
// every few seconds for efficiency.
loggerProvider := log.NewLoggerProvider(
log.WithProcessor(log.NewBatchProcessor(exporter)),
log.WithResource(resource.Default()),
)
// Flush buffered logs before exit — without this, logs still in the
// batch buffer may never be sent. Uses a separate context with a
// timeout so the flush completes even if the main context is cancelled.
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := loggerProvider.Shutdown(shutdownCtx); err != nil {
fmt.Fprintf(os.Stderr, "logger shutdown error: %v\n", err)
}
}()
// Create an slog logger backed by OpenTelemetry. The first argument
// is the instrumentation scope name — use your module path or app name.
logger := slog.New(otelslog.NewHandler("my-app",
otelslog.WithLoggerProvider(loggerProvider),
))
slog.SetDefault(logger)
// Send a test log
slog.Info("Hello from logging.cheap!",
"environment", "production",
"version", "1.0.0",
)
}
Run It
go run .
Logs are buffered and sent in batches. The batch processor flushes automatically every few seconds and on shutdown, so your test log will arrive within about 5 seconds.
Verify in the Log Viewer
Navigate to the log viewer, select a time range that covers the last few minutes,
and search for your test message. You should see your "Hello from logging.cheap!" log entry.
If logs don't appear immediately, wait a few seconds for the batch processor to flush, then refresh the page.
Troubleshooting
- Logs not appearing — check that your environment variables are set in the same shell session where you ran
go run .. Verify the API key starts withcl_live_and includes theBearerprefix in the header value. - Missing
Bearerprefix — theOTEL_EXPORTER_OTLP_HEADERSvalue must beAuthorization=Bearer cl_live_..., not just the key alone. - Wrong exporter package — use
otlploghttp(HTTP transport), nototlploggrpc(gRPC transport). logging.cheap accepts OTLP over HTTP. - Program exits before logs are sent — the batch processor buffers logs and sends them periodically. Always call
loggerProvider.Shutdown()before exit (thedeferblock in the example above handles this). For short-lived programs, Shutdown is essential. - Connection errors — verify that
OTEL_EXPORTER_OTLP_ENDPOINTis set tohttps://logging.cheap. The SDK retries failed requests automatically, so transient network issues are handled.
Next Steps
Your Go application is now sending logs to logging.cheap. In a production application,
initialize the logger provider once at startup and call Shutdown() during
graceful shutdown (e.g. on SIGTERM). All slog calls throughout
your codebase will be sent automatically.
Head to the log viewer to search and explore your logs. For more on the OpenTelemetry Go SDK, see the official OpenTelemetry Go documentation.