otel

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 19 Imported by: 0

README

go-otel 🔭

Tiny OpenTelemetry bootstrap for Go services — one call wires OTLP/gRPC trace and metric exporters into global providers, installs W3C trace-context propagation, and ships instrument helpers plus RED HTTP middleware.

📦 Install

go get github.com/Bugs5382/go-otel

🚀 Usage

Init sets up both traces and metrics on the same OTLP/gRPC endpoint, sharing one resource tagged with service.name. The returned shutdown flushes both pipelines.

shutdown, err := otel.Init(ctx, "my-service", "localhost:4317")
if err != nil {
	log.Fatal(err)
}
defer shutdown(context.Background())

Traces export over OTLP/gRPC (insecure) to the given endpoint. A logs exporter is planned for a later release. 📈

📊 Metrics

After Init, create instruments off the global meter with the helpers and record against them:

requests := otel.Counter("orders.placed", "Orders placed.")
requests.Add(ctx, 1)

latency := otel.Histogram("db.query.duration", "Query duration.", "s")
latency.Record(ctx, 0.042)

Wrap an http.Handler with the middleware to record the RED signals (request rate, errors, duration) using HTTP semantic-convention names — the templated route is picked up automatically from a net/http ServeMux:

mux := http.NewServeMux()
mux.HandleFunc("GET /items/{id}", itemsHandler)

http.ListenAndServe(":8080", otel.Metrics(mux))

🧼 Neutral surface (no raw otel imports)

Counter/Histogram above return the raw go.opentelemetry.io/otel/metric types, and stay that way for existing callers. If you'd rather your own code never imports a go.opentelemetry.io/otel/... package, use the neutral constructors instead — they wrap the same instruments behind interfaces built on a neutral attribute type:

orders := otel.NewCounter("orders.placed", "Orders placed.")
orders.Add(ctx, 1, otel.KV("outcome", "ok"), otel.KV("region", "us-east"))

latency := otel.NewHistogram("db.query.duration", "Query duration.", "s")
latency.Record(ctx, 0.042, otel.KV("outcome", "ok"))

NewCounter/NewHistogram return CounterMetric/HistogramMetric — not Counter/Histogram — because those names are already taken by the raw-returning functions above (a function and a type can't share a name in one Go package). KV(key string, val any) Attr accepts string, bool, int, int64, float64, and slices of those directly; any other value type is rendered with fmt.Sprintf.

🔌 gRPC server instrumentation

GRPCServerStatsHandler/GRPCClientStatsHandler wrap otelgrpc internally and hand back a plain google.golang.org/grpc/stats.Handler, so wiring gRPC tracing/metrics never requires importing otelgrpc (or any other raw otel package) yourself:

s := grpc.NewServer(grpc.StatsHandler(otel.GRPCServerStatsHandler()))

conn, err := grpc.NewClient(target, grpc.WithStatsHandler(otel.GRPCClientStatsHandler()))

🛠 Develop

task build    # go build ./...
task test     # go test ./...
task lint     # gofmt check + golangci-lint + yamllint
task license  # inject MIT headers (golic)

⚖️ License

MIT © 2026 Shane

Documentation

Overview

Package otel wires OTLP gRPC trace and metric exporters into global Tracer and Meter providers from a single Init call, and provides convenience instrument constructors plus RED HTTP middleware. A logs exporter arrives in a later release.

Counter, Histogram, and Metrics return or accept raw go.opentelemetry.io/otel types and remain unchanged for existing callers. A consumer that wants to avoid any raw otel import in its own code should instead use:

  • Attr and KV to build a neutral attribute (no attribute.KeyValue).
  • NewCounter/NewHistogram, returning the neutral CounterMetric/ HistogramMetric interfaces (no metric.Int64Counter/Float64Histogram).
  • GRPCServerStatsHandler/GRPCClientStatsHandler, returning google.golang.org/grpc/stats.Handler (a gRPC transport type, not otel) for grpc.StatsHandler/grpc.WithStatsHandler, so wiring gRPC instrumentation never requires importing otelgrpc directly.

The neutral interfaces are named CounterMetric/HistogramMetric rather than Counter/Histogram because those names are already taken by the raw-returning functions above, and Go does not allow a function and a type to share a name in one package.

Example

Example demonstrates the neutral metrics surface: a consumer records measurements through CounterMetric/HistogramMetric and tags them with Attr, without importing any go.opentelemetry.io/otel package itself.

package main

import (
	"context"
	"fmt"

	gootel "github.com/Bugs5382/go-otel"
)

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

	orders := gootel.NewCounter("orders.placed", "Orders placed.")
	latency := gootel.NewHistogram("db.query.duration", "Query duration.", "s")

	orders.Add(ctx, 1, gootel.KV("outcome", "ok"), gootel.KV("region", "us-east"))
	latency.Record(ctx, 0.042, gootel.KV("outcome", "ok"))

	fmt.Println("recorded")
}
Output:
recorded

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Counter added in v1.1.0

func Counter(name, description string) metric.Int64Counter

Counter returns a monotonic Int64Counter off the global meter. Instrument creation only fails on a malformed name, which is a programming error, so the helper panics rather than forcing every call site to handle an error that cannot occur at runtime.

Counter returns the raw go.opentelemetry.io/otel/metric type, so a caller that wants to avoid a raw otel import in its own code should use NewCounter instead; it wraps the same instrument behind the neutral CounterMetric interface. Counter itself is kept, unchanged, for existing callers.

func GRPCClientStatsHandler added in v1.2.0

func GRPCClientStatsHandler() stats.Handler

GRPCClientStatsHandler returns the client-side counterpart, for grpc.WithStatsHandler on an outbound connection:

conn, err := grpc.NewClient(target, grpc.WithStatsHandler(otel.GRPCClientStatsHandler()))

func GRPCServerStatsHandler added in v1.2.0

func GRPCServerStatsHandler() stats.Handler

GRPCServerStatsHandler returns an otel-instrumented gRPC stats.Handler for a server, wired for grpc.StatsHandler:

s := grpc.NewServer(grpc.StatsHandler(otel.GRPCServerStatsHandler()))

It returns google.golang.org/grpc/stats.Handler — a gRPC transport type, not an otel type — so wiring gRPC instrumentation never requires a caller to import otelgrpc, or any other raw otel package, directly.

Example

ExampleGRPCServerStatsHandler shows wiring gRPC server instrumentation without importing otelgrpc directly: GRPCServerStatsHandler returns a google.golang.org/grpc/stats.Handler that grpc.StatsHandler accepts as-is.

package main

import (
	"fmt"

	gootel "github.com/Bugs5382/go-otel"
	"google.golang.org/grpc"
)

func main() {
	_ = grpc.NewServer(grpc.StatsHandler(gootel.GRPCServerStatsHandler()))
	fmt.Println("server configured")
}
Output:
server configured

func Histogram added in v1.1.0

func Histogram(name, description, unit string) metric.Float64Histogram

Histogram returns a Float64Histogram off the global meter. unit is a UCUM string (for example "s" or "ms"); pass "" for no unit. Like Counter, it panics on the programming-error case of a malformed name.

Histogram returns the raw go.opentelemetry.io/otel/metric type; a caller that wants to avoid a raw otel import in its own code should use NewHistogram instead. Histogram itself is kept, unchanged, for existing callers.

func Init

func Init(ctx context.Context, service, otlpEndpoint string) (shutdown func(context.Context) error, err error)

Init configures global Tracer and Meter providers that export over OTLP gRPC (insecure) to otlpEndpoint, tagged with service.name=service. Traces and metrics ride the same endpoint and share one resource, so a single call wires both. The returned shutdown func flushes and closes both providers; callers should defer it.

func Metrics added in v1.1.0

func Metrics(next http.Handler) http.Handler

Metrics wraps an http.Handler and records the RED signals (request rate, errors, duration) for every request it serves, using OTel HTTP server semantic-convention instrument and attribute names:

  • http.server.request.duration histogram, seconds
  • http.server.request.count counter (rate and errors derive from it)

Each measurement carries http.request.method, http.route, and http.response.status_code attributes. It depends only on net/http, so it composes with any stdlib-compatible router. Call Init first so the instruments record against the global MeterProvider.

Types

type Attr added in v1.2.0

type Attr struct {
	Key string
	Val any
}

Attr is a neutral metric attribute: a key paired with an arbitrary value. It exists so a caller of CounterMetric/HistogramMetric never has to import go.opentelemetry.io/otel/attribute to tag a measurement. Build one with KV.

func KV added in v1.2.0

func KV(key string, val any) Attr

KV builds an Attr from a key and value. Val accepts string, bool, int, int64, float64, and slices of those types directly; any other type is rendered with fmt.Sprintf("%v", ...) rather than rejected, so a caller never has to special-case an attribute value to stay neutral.

type CounterMetric added in v1.2.0

type CounterMetric interface {
	Add(ctx context.Context, n int64, attrs ...Attr)
}

CounterMetric is a neutral monotonic counter: Add records n against attrs. No go.opentelemetry.io/otel type appears in this interface, so a package that only depends on CounterMetric (built via NewCounter) never needs a raw otel import.

The neutral interfaces are named CounterMetric/HistogramMetric, not Counter/Histogram, because Counter and Histogram are already taken by the raw-returning functions above; Go does not allow a function and a type to share a name in one package. Keeping those functions' names stable is the back-compat contract this package makes to existing callers.

func NewCounter added in v1.2.0

func NewCounter(name, description string) CounterMetric

NewCounter builds a neutral CounterMetric off the global meter, wrapping the same instrument Counter builds. Call after Init so measurements export.

type HistogramMetric added in v1.2.0

type HistogramMetric interface {
	Record(ctx context.Context, v float64, attrs ...Attr)
}

HistogramMetric is a neutral value-distribution recorder: Record adds one observation, tagged with attrs. See CounterMetric for why it is not named Histogram.

func NewHistogram added in v1.2.0

func NewHistogram(name, description, unit string) HistogramMetric

NewHistogram builds a neutral HistogramMetric off the global meter, wrapping the same instrument Histogram builds. Call after Init so measurements export.

Jump to

Keyboard shortcuts

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