telemetry

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 15 Imported by: 0

README

telemetry

The telemetry package initialises OpenTelemetry tracing and metrics for a service in a single call. It is intended for services that need distributed tracing via OTLP/gRPC, Prometheus metrics scraping, and a pluggable system for custom metric collectors, without wiring up each OTel component separately in every project.

Import

import "github.com/raykavin/gobox/telemetry"

What it provides

  • New() for installing the global TracerProvider and MeterProvider and starting a Prometheus /metrics HTTP server
  • Collector interface for plugging in custom metric sources (polling loops, external service probes, etc.)
  • OTLP/gRPC trace export with AlwaysSample and W3C TraceContext + Baggage propagation
  • Prometheus metrics with Go runtime and process metrics included by default
  • a single shutdown function that flushes spans, drains the meter, and stops the metrics server

Main types

  • Config: service name, OTLP endpoint, Prometheus port, and optional collectors
  • Collector: interface with RegisterMetrics() error and Start(ctx context.Context)

Quick start

package main

import (
    "context"
    "log"
    "os/signal"
    "syscall"

    "go.opentelemetry.io/otel"
    "github.com/raykavin/gobox/telemetry"
)

func main() {
    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    shutdown, err := telemetry.New(ctx, telemetry.Config{
        ServiceName:  "my-service",
        OTLPEndpoint: "otel-collector:4317",
        MetricsPort:  9090,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer shutdown(ctx)

    // tracing
    tracer := otel.Tracer("my-service/orders")
    ctx2, span := tracer.Start(ctx, "process-order")
    defer span.End()
    _ = ctx2

    // metrics
    meter := otel.Meter("my-service/orders")
    counter, _ := meter.Int64Counter("orders.processed")
    counter.Add(ctx, 1)

    <-ctx.Done()
}

Implementing a custom Collector

A Collector registers its OTel instruments once and optionally runs a background loop to feed them. The example below polls an external HTTP endpoint every 15 seconds and exposes the result as an observable gauge.

package mymetrics

import (
    "context"
    "sync/atomic"
    "time"

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

type ServiceCollector struct {
    up atomic.Int64
}

func (c *ServiceCollector) RegisterMetrics() error {
    meter := otel.GetMeterProvider().Meter("my-service/external")
    _, err := meter.Int64ObservableGauge(
        "external.up",
        metric.WithDescription("1 if the external service is reachable, 0 otherwise"),
        metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error {
            o.Observe(c.up.Load())
            return nil
        }),
    )
    return err
}

func (c *ServiceCollector) Start(ctx context.Context) {
    go func() {
        ticker := time.NewTicker(15 * time.Second)
        defer ticker.Stop()
        for {
            select {
            case <-ctx.Done():
                return
            case <-ticker.C:
                c.up.Store(c.probe(ctx))
            }
        }
    }()
}

func (c *ServiceCollector) probe(ctx context.Context) int64 {
    // ... check the external service ...
    return 1
}

Register it alongside New:

shutdown, err := telemetry.New(ctx, telemetry.Config{
    ServiceName: "my-service",
    MetricsPort: 9090,
    Collectors:  []telemetry.Collector{&mymetrics.ServiceCollector{}},
})

Prometheus endpoint

The /metrics endpoint is served on MetricsPort and includes:

  • Go runtime metrics (goroutines, heap, GC cycles, etc.) via collectors.NewGoCollector
  • Process metrics (CPU, file descriptors, resident memory) via collectors.NewProcessCollector
  • All OTel instruments registered through the global MeterProvider

Notes

  • OTLPEndpoint defaults to the OTel SDK default (localhost:4317) when empty
  • the trace exporter uses an insecure gRPC connection; add TLS by customising newTracerProvider if needed
  • a Collector that returns an error from RegisterMetrics is silently skipped; Start is not called for it
  • Start must return immediately; long-running work must run in a goroutine bound to the supplied context
  • the shutdown function is always safe to call, even when New returned an error

Documentation

Overview

Package telemetry initialises OpenTelemetry tracing and metrics for a service in a single call, exposing a Prometheus /metrics endpoint and sending traces to an OTLP/gRPC collector.

Initialisation

Call New once at startup. It installs the global TracerProvider and MeterProvider, starts the Prometheus HTTP server, and returns a shutdown function that must be called on exit to flush pending spans.

shutdown, err := telemetry.New(ctx, telemetry.Config{
    ServiceName:  "my-service",
    OTLPEndpoint: "otel-collector:4317",
    MetricsPort:  9090,
})
if err != nil {
    log.Fatal(err)
}
defer shutdown(ctx)

Tracing

After New returns, use the global tracer from anywhere in the application.

tracer := otel.Tracer("my-service/orders")
ctx, span := tracer.Start(ctx, "process-order")
defer span.End()

Metrics

After New returns, use the global meter from anywhere in the application.

meter := otel.Meter("my-service/orders")
counter, _ := meter.Int64Counter("orders.processed")
counter.Add(ctx, 1)

Pluggable collectors

Custom metric sources implement the Collector interface and are passed in Config.Collectors. Each collector registers its instruments once and optionally runs a background polling loop.

shutdown, err := telemetry.New(ctx, telemetry.Config{
    ServiceName: "my-service",
    MetricsPort: 9090,
    Collectors:  []telemetry.Collector{myCollector},
})

A collector that fails RegisterMetrics is skipped without stopping the rest of the subsystem.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(ctx context.Context, cfg Config) (shutdown func(context.Context), err error)

New initialises the global TracerProvider and MeterProvider, then starts a dedicated HTTP server on cfg.MetricsPort exposing /metrics for Prometheus. Any collectors supplied in cfg.Collectors are registered and started.

The returned shutdown function must be called on application exit to flush pending spans and stop the metrics server. It is safe to call even if New returned an error (it becomes a no-op).

Types

type Collector

type Collector interface {
	// RegisterMetrics registers the collector's instruments. It is called
	// once during New, after the MeterProvider is set globally.
	RegisterMetrics() error

	// Start launches the collector's background work, if any. It must not
	// block; long-running work belongs in a goroutine bound to ctx.
	Start(ctx context.Context)
}

Collector is a pluggable source of custom metrics. Implementations register their instruments against the global MeterProvider (already configured by New) and optionally run a background loop to feed them.

The lifecycle is:

  1. RegisterMetrics is called once, after the MeterProvider is installed but before Start. Returning an error aborts the collector (Start is skipped) without affecting the rest of the telemetry subsystem.
  2. Start is called with a context tied to the telemetry lifetime; the collector should return promptly, spawning any background work as a goroutine that exits when ctx is cancelled.

A typical implementation polls an external system on a ticker and exposes the latest snapshot through OTel observable instruments.

type Config

type Config struct {
	// ServiceName identifies this service in traces and metrics. Required.
	ServiceName string

	// OTLPEndpoint is the OTLP/gRPC collector address (host:port). When empty,
	// the exporter's default endpoint is used.
	OTLPEndpoint string

	// MetricsPort is the TCP port for the Prometheus /metrics HTTP server.
	MetricsPort uint16

	// Collectors are optional pluggable metric sources. Each is registered and
	// started during New, after the global providers are installed. A
	// collector whose RegisterMetrics fails is skipped without aborting the
	// rest of the subsystem.
	Collectors []Collector
}

Config holds the runtime settings for the telemetry subsystem.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL