SigNoz
Docs
PricingCustomers
Get Started - Free
Docs
IntroductionContributingMigrate from DatadogSigNoz API
OpenTelemetry
What is OpenTelemetryOpenTelemetry Collector GuideOpenTelemetry Demo
Community
Support
Slack
X
Launch Week
Changelog
Dashboard Templates
DevOps Wordle
Newsletter
KubeCon, Atlanta 2025
More
SigNoz vs DatadogSigNoz vs New RelicSigNoz vs GrafanaSigNoz vs Dynatrace
Careers
AboutTermsPrivacySecurity & Compliance
SigNoz Logo
SigNoz
All systems operational
HIPAASOC-2
SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Send Logs from log/slog to SigNoz

Send logs from Go's standard log/slog package to SigNoz using the OpenTelemetry otelslog bridge.

Most steps are identical. Update the endpoint and remove the ingestion key header as shown in Cloud → Self-Hosted.

Prerequisites

  • Go 1.21 or later (log/slog is a Go 1.21 standard library package)
  • An instance of SigNoz (either Cloud or Self-Hosted)

Steps

Step 1: Install dependencies

Install the required modules:

go get go.opentelemetry.io/contrib/bridges/otelslog
go get go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
go get go.opentelemetry.io/otel/sdk/log
  • otelslog: bridges log/slog to the OpenTelemetry log pipeline
  • otlploghttp: OTLP exporter that ships logs over HTTP to SigNoz
  • sdk/log: log processor and provider

Step 2: Set environment variables

export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_SERVICE_NAME="<service_name>"

Verify these values:

  • <region>: Your SigNoz Cloud region.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service_name>: Your service name.

Add these environment variables to your deployment manifest:

env:
  - name: OTEL_EXPORTER_OTLP_ENDPOINT
    value: 'https://ingest.<region>.signoz.cloud:443'
  - name: OTEL_EXPORTER_OTLP_HEADERS
    value: 'signoz-ingestion-key=<your-ingestion-key>'
  - name: OTEL_SERVICE_NAME
    value: '<service_name>'

Verify these values:

  • <region>: Your SigNoz Cloud region.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service_name>: Your service name.

If using the k8s-infra chart (which auto-collects container logs), disable log collection for this application to prevent duplicates.

See how to disable log collection →

Pass environment variables at runtime:

docker run -e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
    -e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
    -e OTEL_SERVICE_NAME="<service_name>" \
    your-image:latest

For deployed containers, inject these values through your container platform's environment variable or secret manager.

Verify these values:

  • <region>: Your SigNoz Cloud region.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service_name>: Your service name.
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:OTEL_SERVICE_NAME = "<service_name>"

Verify these values:

  • <region>: Your SigNoz Cloud region.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service_name>: Your service name.

Step 3: Create an exporter

otlploghttp.New(ctx) reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS from the environment automatically.

main.go
ctx := context.Background()

exporter, err := otlploghttp.New(ctx)
if err != nil {
	fmt.Println("failed to create exporter:", err)
	return
}

Step 4: Create a log processor and provider

main.go
processor := log.NewBatchProcessor(exporter)

provider := log.NewLoggerProvider(
	log.WithProcessor(processor),
)
defer func() {
	if err := provider.Shutdown(context.Background()); err != nil {
		fmt.Println(err)
	}
}()

NewBatchProcessor batches log records before export. Always defer provider.Shutdown so pending records flush before the process exits.

Step 5: Initialize the slog logger

main.go
logger := otelslog.NewLogger("my/pkg/name", otelslog.WithLoggerProvider(provider))

otelslog.NewLogger returns a standard *slog.Logger backed by the OpenTelemetry log pipeline. Use it anywhere you would use slog.Default().

Step 6: Log messages

main.go
logger.Info("server listening", "addr", ":8080", "env", "production", "version", "v2.4.1")
logger.Info("order received", "order_id", "ord-8821", "customer_id", "cust-441", "amount_usd", 149.99)
logger.Info("payment authorized", "order_id", "ord-8821", "gateway", "stripe", "duration_ms", 212)
logger.Warn("inventory below threshold", "sku", "SNZ-PRO-12", "stock", 4, "reorder_point", 10)
logger.Info("order dispatched", "order_id", "ord-8821", "carrier", "fedex", "tracking_id", "FX-99102847")
logger.Error("notification delivery failed", "order_id", "ord-8821", "channel", "email", "error", "smtp: connection timeout")

Complete example

main.go
package main

import (
	"context"
	"fmt"

	"go.opentelemetry.io/contrib/bridges/otelslog"
	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
	"go.opentelemetry.io/otel/sdk/log"
)

func main() {
	// Reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS from the environment.
	// ctx here is used only to initialize the exporter; it carries no span,
	// so logs emitted against this context will not be correlated with a trace.
	ctx := context.Background()

	exporter, err := otlploghttp.New(ctx)
	if err != nil {
		fmt.Println("failed to create exporter:", err)
		return
	}

	processor := log.NewBatchProcessor(exporter)

	provider := log.NewLoggerProvider(
		log.WithProcessor(processor),
	)
	defer func() {
		if err := provider.Shutdown(context.Background()); err != nil {
			fmt.Println(err)
		}
	}()

	logger := otelslog.NewLogger("my/pkg/name", otelslog.WithLoggerProvider(provider))

	logger.Info("server listening", "addr", ":8080", "env", "production", "version", "v2.4.1")
	logger.Info("order received", "order_id", "ord-8821", "customer_id", "cust-441", "amount_usd", 149.99)
	logger.Info("payment authorized", "order_id", "ord-8821", "gateway", "stripe", "duration_ms", 212)
	logger.Warn("inventory below threshold", "sku", "SNZ-PRO-12", "stock", 4, "reorder_point", 10)
	logger.Info("order dispatched", "order_id", "ord-8821", "carrier", "fedex", "tracking_id", "FX-99102847")
	logger.Error("notification delivery failed", "order_id", "ord-8821", "channel", "email", "error", "smtp: connection timeout")
}

Validate

Captured logs can be viewed in the Logs Explorer section.

Sample slog logs in SigNoz Logs Explorer
Sample slog logs in SigNoz Logs Explorer

Correlating Traces and Logs

Install the additional dependency:

go get go.opentelemetry.io/otel/trace

Pass a context with an active span and the SDK sets trace_id and span_id on the log record automatically. No manual extraction needed.

Use InfoContext, WarnContext, and ErrorContext to carry trace context in logs:

handler.go
// Inside a handler or function whose ctx carries an active span:
logger.InfoContext(ctx, "request received", "method", r.Method, "path", r.URL.Path)
logger.ErrorContext(ctx, "upstream call failed", "service", "payments", "error", err)

For ctx to carry an active span, your code must run inside a tracer started with tracer.Start(ctx, ...) or inside an HTTP handler wrapped by otelhttp.NewHandler. Calling with context.Background() produces no trace_id or span_id.

Example HTTP handler:

handler.go
import (
	"net/http"
)

func (s *Server) handleOrder(w http.ResponseWriter, r *http.Request) {
	// r.Context() carries the active span injected by the otelhttp middleware.
	logger.InfoContext(r.Context(), "processing order", "order_id", r.URL.Query().Get("id"))
	// ... handler logic
}

For HTTP tracing setup, see Instrument a Go HTTP server.

To jump between correlated logs and traces in SigNoz, see Correlate Traces and Logs.

Troubleshooting

Logs not appearing in SigNoz

  • Confirm OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS are set and correct.
  • Confirm provider.Shutdown runs before process exit — buffered logs flush at shutdown.
  • If the process calls os.Exit directly, deferred cleanup is skipped. Call provider.Shutdown explicitly before exiting instead of relying on defer.

trace_id and span_id are empty

  • Confirm you are calling logger.InfoContext(ctx, ...) not logger.Info(...).
  • Confirm ctx carries a recording span. Spans started by tracer.Start are recording by default. context.Background() carries no span.
  • Confirm your OTel tracer is initialized before the first request. See OpenTelemetry Go instrumentation.

Next Steps

  • Correlate Traces and Logs
  • Explore logs in SigNoz
  • Create log-based alerts

Get Help

If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack.

If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.

Last updated: May 19, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.

Prev
Zap
Next
Deno
On this page
Prerequisites
Steps
Step 1: Install dependencies
Step 2: Set environment variables
Step 3: Create an exporter
Step 4: Create a log processor and provider
Step 5: Initialize the slog logger
Step 6: Log messages
Complete example
Validate
Correlating Traces and Logs
Troubleshooting
Logs not appearing in SigNoz
trace_id and span_id are empty
Next Steps
Get Help

Is this page helpful?

Your response helps us improve this page.