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

Send Metrics from Go application

This guide shows you how to instrument your Go application with OpenTelemetry to send metrics to SigNoz. You will learn how to collect runtime metrics, auto-instrument libraries, and create custom metrics.

Prerequisites

  • Go 1.21 or later
  • A SigNoz Cloud account or self-hosted SigNoz instance
  • Your application code

Send metrics to SigNoz

Step 1. Set environment variables

Set the following environment variables to configure the OpenTelemetry exporter:

export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_METRICS_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_RESOURCE_ATTRIBUTES="service.name=<service-name>"

Replace the following:

  • <region>: Your SigNoz Cloud region (us, eu, or in). See endpoints.
  • <your-ingestion-key>: Your SigNoz ingestion key.
  • <service-name>: A descriptive name for your service (e.g., payment-service).

Step 2. Install OpenTelemetry packages

Run the following command in your project directory to install the necessary packages for metrics and OTLP export:

go get \
go.opentelemetry.io/otel \
go.opentelemetry.io/otel/sdk/metric \
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc \
go.opentelemetry.io/otel/attribute

Step 3. Initialize the Meter Provider

Create a helper function to configure the OpenTelemetry Meter Provider. This provider is responsible for creating meters and exporting metrics. It automatically uses the environment variables configured in Step 1.

main.go
package main

import (
	"context"
	"fmt"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
)

func initMeterProvider(ctx context.Context) (func(context.Context) error, error) {
	// Create the OTLP exporter
	// It will automatically use OTEL_EXPORTER_OTLP_METRICS_ENDPOINT and headers from env
	exporter, err := otlpmetricgrpc.New(ctx)
	if err != nil {
		return nil, fmt.Errorf("new otlp metric grpc exporter failed: %w", err)
	}

	// Create the resource with attributes from environment (OTEL_RESOURCE_ATTRIBUTES)
	// and default host/process attributes
	res, err := resource.New(ctx,
		resource.WithFromEnv(),
		resource.WithHost(),
		resource.WithProcess(),
		resource.WithOS(),
	)
	if err != nil {
		return nil, fmt.Errorf("new resource failed: %w", err)
	}

	// Create the MeterProvider with the exporter and resource
	// Set a periodic reader to export metrics every 10 seconds
	mp := metric.NewMeterProvider(
		metric.WithResource(res),
		metric.WithReader(metric.NewPeriodicReader(exporter, metric.WithInterval(10*time.Second))),
	)

	// Set the global MeterProvider
	otel.SetMeterProvider(mp)

	return mp.Shutdown, nil
}

Step 4. Instrument your application

Here's a complete example that tracks HTTP requests with a counter metric:

main.go
package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/metric"
)

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

	// Initialize Meter Provider
	shutdown, err := initMeterProvider(ctx)
	if err != nil {
		log.Fatalf("Failed to initialize meter provider: %v", err)
	}
	defer func() {
		if err := shutdown(context.Background()); err != nil {
			log.Printf("Error shutting down meter provider: %v", err)
		}
	}()

	// Get a meter from the global provider
	meter := otel.Meter("my-app-meter")

	// Define a counter metric
	reqCounter, err := meter.Int64Counter(
		"http.requests",
		metric.WithDescription("Total request count"),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Handler with Instrumentation
	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Simulate work
		time.Sleep(50 * time.Millisecond)
		w.Write([]byte("ok"))

		// Record Count
		reqCounter.Add(ctx, 1, metric.WithAttributes(
			attribute.String("method", r.Method),
			attribute.String("route", "/hello"),
			attribute.Int("status", 200),
		))

		log.Printf("Request completed in %v", time.Since(start))
	})

	log.Println("Server listening on :8080...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Library instrumentation

You can use OpenTelemetry's auto-instrumentation packages and middleware for popular Go libraries to automatically collect metrics, or create custom metrics manually.

The runtime package provides auto-instrumentation for Go runtime metrics such as memory usage, goroutine count, and GC stats.

Install

go get go.opentelemetry.io/contrib/instrumentation/runtime

Usage

main.go
package main

import (
	"context"
	"log"

	"go.opentelemetry.io/contrib/instrumentation/runtime"
	"go.opentelemetry.io/otel"
)

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

	// Initialize Meter Provider (uses env vars from Step 1)
	shutdown, err := initMeterProvider(ctx)
	if err != nil {
		log.Fatalf("Failed to initialize meter provider: %v", err)
	}
	defer func() {
		if err := shutdown(context.Background()); err != nil {
			log.Printf("Error shutting down meter provider: %v", err)
		}
	}()

	// Auto Go runtime metrics (go.memory.used, go.goroutine.count, ...)
	if err := runtime.Start(runtime.WithMeterProvider(otel.GetMeterProvider())); err != nil {
		log.Fatal(err)
	}

	log.Println("Runtime metrics collection started...")
	select {}
}

You can also use the pre-configured dashboard to monitor golang runtime metrics.

Exported Metrics

The runtime package automatically exports the following Go runtime metrics:

Memory Metrics:

  • go.memory.used (UpDownCounter) - Memory used by the Go runtime
  • go.memory.limit (UpDownCounter) - Go runtime memory limit configured by the user, if a limit exists
  • go.memory.allocated (Counter) - Memory allocated to the heap by the application
  • go.memory.allocations (Counter) - Count of allocations to the heap by the application
  • go.memory.gc.goal (UpDownCounter) - Heap size target for the end of the GC cycle

Concurrency Metrics:

  • go.goroutine.count (UpDownCounter) - Count of live goroutines
  • go.processor.limit (UpDownCounter) - The number of OS threads that can execute user-level Go code simultaneously

Configuration Metrics:

  • go.config.gogc (UpDownCounter) - Heap size target percentage configured by the user (default: 100)

Scheduling Metrics:

  • go.schedule.duration (Histogram) - Time goroutines spent in the scheduler in a runnable state before running
Info

Metric names and types follow the OpenTelemetry semantic conventions for Go runtime metrics. For the complete specification, see the Go runtime semantic conventions.

Validate

Once you have configured your application to start sending metrics to SigNoz, you can start visualizing the metrics in the metrics explorer.

SigNoz Metrics Explorer
Visualizing Go metrics in SigNoz

Troubleshooting

Metrics not appearing?

  1. Check Environment Variables: Ensure OTEL_EXPORTER_OTLP_METRICS_ENDPOINT is set correctly:

    • For gRPC (default in this guide): https://ingest.<region>.signoz.cloud:443
    • For HTTP (if using otlpmetrichttp): https://ingest.<region>.signoz.cloud:443/v1/metrics
  2. Check Exporter Protocol: This guide uses otlpmetricgrpc. If you are behind a proxy that only supports HTTP/1.1, switch to otlpmetrichttp.

  3. Check Console Errors: The OpenTelemetry SDK prints errors to stderr by default. Check your application logs for any connection refused or authentication errors.

  4. Resource Attributes: Ensure service.name is set. This helps you filter metrics by service in SigNoz.

Authentication errors

If you see errors like "Unauthorized" or "403 Forbidden":

  • Verify your ingestion key is correct in OTEL_EXPORTER_OTLP_METRICS_HEADERS
  • Ensure the header format is exactly: signoz-ingestion-key=<your-key> (no extra spaces)
  • Check that your ingestion key is active in the SigNoz Cloud dashboard

"Connection Refused" errors

  • If running locally and sending to SigNoz Cloud, check your internet connection and firewall.
  • If sending to a self-hosted collector, ensure the collector is running and listening on port 4317 (gRPC) or 4318 (HTTP).

Setup OpenTelemetry Collector (Optional)

What is the OpenTelemetry Collector?

Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.

Why use it?

  • Cleaning up data — Filter out noisy traces you don't care about, or remove sensitive info before it leaves your servers.
  • Keeping your app lightweight — Let the Collector handle batching, retries, and compression instead of your application code.
  • Adding context automatically — The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
  • Future flexibility — Want to send data to multiple backends later? The Collector makes that easy without changing your app.

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.

Next Steps

Last updated: December 2, 2025

Edit on GitHub

Was this page helpful?