SigNoz Cloud - This page is relevant for SigNoz Cloud editions.
Self-Host - This page is relevant for self-hosted SigNoz editions.

Send logs from Logrus to SigNoz using OpenTelemetry

Prerequisites

Send logs to SigNoz

Step 1. 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:

Step 2. Install dependencies

In the root folder of your Go application, run:

go get \
  github.com/sirupsen/logrus \
  go.opentelemetry.io/contrib/bridges/otellogrus \
  go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp \
  go.opentelemetry.io/otel/sdk/log

Step 3. Instrument your application

Add the following code to your main.go file:

main.go
package main

import (
	"context"
	"log"

	"github.com/sirupsen/logrus"
	"go.opentelemetry.io/contrib/bridges/otellogrus"
	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
	otel_log "go.opentelemetry.io/otel/sdk/log"
)

func main() {
	ctx := context.Background()

	// Reads OTEL_EXPORTER_OTLP_ENDPOINT from env and initializes the OTLP HTTP log exporter
	logExporter, err := otlploghttp.New(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// Wraps the exporter in a LoggerProvider with a batch processor
	logProvider := otel_log.NewLoggerProvider(
		otel_log.WithProcessor(
			otel_log.NewBatchProcessor(logExporter),
		),
	)
	defer logProvider.Shutdown(ctx)

	// Registers the hook globally. All logrus.Info/Warn/Error calls are exported as OTel logs
	hook := otellogrus.NewHook("signoz-golang", otellogrus.WithLoggerProvider(logProvider))
	logrus.AddHook(hook)

	logrus.Info("Hello from Logrus via OpenTelemetry")
}
  1. Creates an OTLP HTTP log exporter that sends logs to the endpoint defined by OTEL_EXPORTER_OTLP_ENDPOINT.
  2. Wraps the exporter in a LoggerProvider with a batch processor for efficient log delivery.
  3. Creates a Logrus hook via otellogrus.NewHook and registers it globally with logrus.AddHook.
  4. Every logrus.Info, logrus.Warn, logrus.Error, etc. call is now captured and exported as OpenTelemetry logs.

Step 4. Run your application

go run main.go

The environment variables you set in Step 1 are automatically read by the OTLP exporter.

With Trace Correlation (Advanced)

Correlate Logrus logs with OpenTelemetry traces by injecting trace_id and span_id fields into your structured logs.

Install additional dependencies

go get \
  github.com/sirupsen/logrus \
  go.opentelemetry.io/contrib/bridges/otellogrus \
  go.opentelemetry.io/otel \
  go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp \
  go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \
  go.opentelemetry.io/otel/sdk/log \
  go.opentelemetry.io/otel/sdk/trace \
  go.opentelemetry.io/otel/trace

Instrument your application

Add the following code to your main.go file:

main.go
package main

import (
	"context"
	"log"

	"github.com/sirupsen/logrus"
	"go.opentelemetry.io/contrib/bridges/otellogrus"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	otel_log "go.opentelemetry.io/otel/sdk/log"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	oteltrace "go.opentelemetry.io/otel/trace"
)

// Extracts trace correlation details from an OpenTelemetry span
func LogrusFields(span oteltrace.Span) logrus.Fields {
	return logrus.Fields{
		"span_id":  span.SpanContext().SpanID().String(),
		"trace_id": span.SpanContext().TraceID().String(),
	}
}

func main() {
	ctx := context.Background()

	// Reads OTEL_EXPORTER_OTLP_ENDPOINT from env and initializes the OTLP HTTP log exporter
	logExporter, err := otlploghttp.New(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// Wraps the exporter in a LoggerProvider with a batch processor
	logProvider := otel_log.NewLoggerProvider(
		otel_log.WithProcessor(
			otel_log.NewBatchProcessor(logExporter),
		),
	)
	defer logProvider.Shutdown(ctx)

	// Registers the hook globally. All logrus.Info/Warn/Error calls are exported as OTel logs
	hook := otellogrus.NewHook("signoz-golang", otellogrus.WithLoggerProvider(logProvider))
	logrus.AddHook(hook)

	// Initializes the OTLP HTTP trace exporter
	traceExporter, err := otlptracehttp.New(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// Configures the TracerProvider with a batch processor and always sample
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithBatcher(traceExporter),
	)
	defer tp.Shutdown(ctx)

	// Registers the TracerProvider globally
	otel.SetTracerProvider(tp)

	// Starts a sample span
	tracer := otel.GetTracerProvider().Tracer("signoz-tracer")
	ctx, span := tracer.Start(ctx, "signoz-span")
	defer span.End()

	// Injects trace correlation details into structured log fields
	logrus.WithContext(ctx).WithFields(LogrusFields(span)).Info("Logrus log with trace correlation")
	logrus.Info("Logrus log without trace correlation")
}
  1. Sets up both an OTLP log exporter and an OTLP trace exporter.
  2. Creates a LoggerProvider for logs and a TracerProvider for traces.
  3. Registers a Logrus hook to send all logs through OpenTelemetry.
  4. Starts a span and extracts its trace_id and span_id via LogrusFields.
  5. Uses logrus.WithContext(ctx).WithFields(...) to attach trace IDs as structured fields to the log entry.

Run your application

go run main.go

The environment variables you set in Step 1 are automatically read by the OTLP exporter.

Validate

Open the Logs section in your SigNoz dashboard. You should see log entries from your Logrus logger.

If you followed the With Trace Correlation section, the trace_id and span_id fields appear as indexed attributes on each log entry, letting you jump from a log to its corresponding trace.

Logrus log with trace correlation in SigNoz
Logrus log entry with trace_id and span_id fields for trace correlation

Setup OpenTelemetry Collector (Optional)

What is the OpenTelemetry Collector?

The OTel Collector sits between your application and SigNoz. Your app sends telemetry to the Collector, which then forwards it to SigNoz.

Why use it?

  • Filtering and sampling — Drop noisy logs or sample traces before they leave your infrastructure.
  • Lightweight applications — Offload batching, retries, and compression to the Collector.
  • Resource enrichment — The Collector can tag data with Kubernetes pod names, cloud regions, or host metadata.
  • Multi-backend routing — Send telemetry to multiple destinations without changing your application code.

Collector configuration

The default Collector configuration already includes the otlp receiver. Point your application to the Collector endpoint:

OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" \
OTEL_SERVICE_NAME="<service_name>" \
go run main.go

See Switch from direct export to Collector for step-by-step instructions to convert your setup.

For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.

Troubleshooting

No logs appearing in SigNoz

  • Verify OTEL_EXPORTER_OTLP_ENDPOINT points to the correct endpoint and port (443 for Cloud, 4318 for self-hosted).
  • For SigNoz Cloud, confirm OTEL_EXPORTER_OTLP_HEADERS contains a valid ingestion key.
  • Ensure defer logProvider.Shutdown(ctx) is called before your application exits. Without it, buffered logs may not flush.
  • Check that your application emits logs via logrus.Info, logrus.Error, etc. after the hook is registered.

Trace IDs not appearing in logs

  • Confirm you are using logrus.WithContext(ctx).WithFields(LogrusFields(span)) and not just logrus.Info().
  • Verify the span is active and not already ended when the log is emitted.
  • Check that the oteltrace import is "go.opentelemetry.io/otel/trace" and LogrusFields correctly extracts span.SpanContext().

Next Steps

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: April 13, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.