Prerequisites
- A running Golang application that uses the Logrus logger
- An instance of SigNoz (either Cloud or Self-Hosted)
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:
<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your SigNoz ingestion key.<service_name>: Your service name.
Step 1. Set environment variables
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.
Step 1. Set environment variables in Dockerfile
Add environment variables to your Dockerfile:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
FROM alpine:latest
WORKDIR /app
COPY /app/main .
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>"
CMD ["./main"]
Or pass them 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
Verify these values:
<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your SigNoz ingestion key.<service_name>: Your service name.
Step 1. Set environment variables (PowerShell)
$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 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:
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")
}
- Creates an OTLP HTTP log exporter that sends logs to the endpoint defined by
OTEL_EXPORTER_OTLP_ENDPOINT. - Wraps the exporter in a
LoggerProviderwith a batch processor for efficient log delivery. - Creates a Logrus hook via
otellogrus.NewHookand registers it globally withlogrus.AddHook. - 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:
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")
}
- Sets up both an OTLP log exporter and an OTLP trace exporter.
- Creates a
LoggerProviderfor logs and aTracerProviderfor traces. - Registers a Logrus hook to send all logs through OpenTelemetry.
- Starts a span and extracts its
trace_idandspan_idviaLogrusFields. - 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.

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_ENDPOINTpoints to the correct endpoint and port (443for Cloud,4318for self-hosted). - For SigNoz Cloud, confirm
OTEL_EXPORTER_OTLP_HEADERScontains 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 justlogrus.Info(). - Verify the span is active and not already ended when the log is emitted.
- Check that the
oteltraceimport is"go.opentelemetry.io/otel/trace"andLogrusFieldscorrectly extractsspan.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.