metry

package module
v0.2.7 Latest Latest
Warning

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

Go to latest
Published: Mar 30, 2026 License: MIT Imports: 15 Imported by: 0

README

metry

Go OpenTelemetry

Universal, low-boilerplate OpenTelemetry and GenAI telemetry toolkit for Go.

Installation

go get github.com/skosovsky/metry
go get github.com/skosovsky/metry/middleware/grpc

Use matching versions of metry and metry/middleware/grpc after the stateless API break.

Quick start

package main

import (
    "context"

    "github.com/skosovsky/metry"
    "github.com/skosovsky/metry/genai"
)

func setup(ctx context.Context) (*metry.Provider, *genai.Tracker, error) {
    provider, err := metry.New(
        ctx,
        metry.WithServiceName("my-ai-service"),
        metry.WithTraceRatio(1.0),
        // Optional: override ratio with a custom head sampler.
        // metry.WithSampler(genai.NewHintSampler(sdktrace.TraceIDRatioBased(0.1))),
        // metry.WithExporter(...),
        // metry.WithMetricExporter(...),
    )
    if err != nil {
        return nil, nil, err
    }

    tracker, err := genai.NewTracker(
        provider.MeterProvider.Meter("metry/genai"),
        provider.TracerProvider.Tracer("metry/genai"),
        genai.WithRecordPayloads(true),
    )
    if err != nil {
        _ = provider.Shutdown(ctx)
        return nil, nil, err
    }

    return provider, tracker, nil
}

metry.New(...) is stateless and does not mutate global OTel providers. WithSampler(...) is head-based and takes precedence over WithTraceRatio(...).

Provider lifecycle

provider, tracker, err := setup(ctx)
if err != nil {
    return err
}
defer provider.Shutdown(ctx)

_, span := provider.TracerProvider.Tracer("app/handler").Start(ctx, "request")
tracker.RecordInteraction(ctx, span,
    genai.Meta{
        Provider:      "openai",
        Operation:     "chat",
        RequestModel:  "gpt-4o-mini",
        ResponseModel: "gpt-4o-mini",
    },
    genai.Payload{
        InputMessages: []genai.Message{{
            Role: "user",
            Parts: []genai.ContentPart{{Type: "text", Content: "Summarize this"}},
        }},
    },
    genai.Usage{InputTokens: 150, OutputTokens: 50, Cost: 0.002},
)
span.End()

GenAI API

Runtime operations are available only as tracker instance methods:

  • (*Tracker).RecordInteraction
  • (*Tracker).RecordTTFT
  • (*Tracker).RecordStreamingCompletion
  • (*Tracker).StartToolSpan
  • (*Tracker).RecordToolResult
  • (*Tracker).RecordAsyncFeedback
  • (*Tracker).StartRetrievalSpan
  • (*Tracker).RecordRetrievalResult
  • (*Tracker).RecordEvaluations

Stateless helpers remain package-level:

  • genai.RecordCacheHit
  • genai.RecordAgentStep

genai.NewTracker requires both meter and tracer explicitly. Use go.opentelemetry.io/otel/metric/noop for traces-only setups. If you need traces only, pass a noop meter provider:

import noopmetric "go.opentelemetry.io/otel/metric/noop"

noopMeter := noopmetric.NewMeterProvider().Meter("metry/genai")
tracker, err := genai.NewTracker(noopMeter, provider.TracerProvider.Tracer("metry/genai"))

Sampling hints (head-based)

Snippet note: examples in this section assume imports for go.opentelemetry.io/otel/trace and go.opentelemetry.io/otel/sdk/trace.

provider, err := metry.New(
    ctx,
    metry.WithServiceName("my-ai-service"),
    metry.WithSampler(genai.NewHintSampler(sdktrace.TraceIDRatioBased(0.1))),
)
if err != nil {
    return err
}

_, span := provider.TracerProvider.Tracer("app").Start(
    ctx,
    "request",
    trace.WithAttributes(genai.SamplingKeepKey.Bool(true)),
)
span.End()

gen_ai.sampling.keep=true is evaluated at span start in the SDK sampler. It does not depend on post-hoc status, token usage, or tail sampling. Without keep hint, sampled parent context is inherited; the base sampler is consulted for new root spans. Helpers that create spans internally also accept trace.SpanStartOption, so the same hint can be passed through StartToolSpan(...), StartRetrievalSpan(...), RecordAsyncFeedback(...), and RecordEvaluations(...). When caller options contain duplicate attribute keys, helper built-in keys win.

Retrieval spans

Snippet note: this example also assumes import of go.opentelemetry.io/otel/codes.

retrievalCtx, retrievalSpan := tracker.StartRetrievalSpan(ctx, "vector.search", genai.RetrievalRequest{
    Provider: "qdrant",
    Source:   "knowledge_base",
    Query:    query,
    TopK:     5,
}, trace.WithAttributes(genai.SamplingKeepKey.Bool(true)))
defer retrievalSpan.End()

chunks, distances, err := retriever.Search(retrievalCtx, query)
if err != nil {
    retrievalSpan.RecordError(err)
    retrievalSpan.SetStatus(codes.Error, "retrieval failed")
    return err
}

tracker.RecordRetrievalResult(retrievalSpan, genai.RetrievalResult{
    ReturnedChunks: len(chunks),
    Distances:      distances,
})

Async feedback

parent := trace.NewSpanContext(trace.SpanContextConfig{
    TraceID: traceID,
    SpanID:  spanID,
    Remote:  true,
})

if err := tracker.RecordAsyncFeedback(
    ctx,
    parent,
    0.9,
    "approved",
    trace.WithAttributes(genai.SamplingKeepKey.Bool(true)),
); err != nil {
    return err
}

RecordAsyncFeedback requires a valid parent trace.SpanContext; it does not generate synthetic span IDs.

LLM evaluations

if err := tracker.RecordEvaluations(
    ctx,
    parent,
    []genai.Evaluation{
        {
            Metric:    genai.EvaluationFaithfulness,
            Score:     0.91,
            Reasoning: "Grounded in retrieved chunks.",
        },
        {
            Metric: genai.EvaluationAnswerRelevance,
            Score:  0.84,
        },
    },
    trace.WithAttributes(genai.SamplingKeepKey.Bool(true)),
); err != nil {
    return err
}

Payload truncation

Payload/tool attributes are written as strings and truncated with UTF-8-safe truncation.

  • Default max length: 65536 bytes.
  • Configure via genai.WithMaxContextLength(bytes).
  • Span event text (e.g. LLM-judge reasoning on evaluation events when payloads are enabled) uses a separate, smaller default (4096 bytes) via genai.WithMaxEventLength, because backends often cap event attributes more strictly than span attributes.
  • Truncated values may be non-valid JSON strings by design.

HTTP middleware

import metryhttp "github.com/skosovsky/metry/middleware/http"

handler := metryhttp.Handler(provider, next, "http.request")

gRPC middleware

import metrygrpc "github.com/skosovsky/metry/middleware/grpc"

server := grpc.NewServer(metrygrpc.ServerOptions(
    provider,
)...)
conn, err := grpc.NewClient(addr, metrygrpc.ClientDialOption(
    provider,
))

Testing

make test runs tests for all modules.

License

MIT

Documentation

Overview

Package metry provides a zero-boilerplate OpenTelemetry and LLMOps hub for Go AI applications.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrServiceNameRequired is returned when New is called with an empty ServiceName.
	ErrServiceNameRequired = errors.New("metry: ServiceName is required")
)

Functions

func BaggageValue

func BaggageValue(ctx context.Context, key string) string

BaggageValue retrieves a value for the given key from the context's baggage. Returns an empty string if the key does not exist.

func SetBaggageValue added in v0.1.3

func SetBaggageValue(ctx context.Context, key, value string) (context.Context, error)

SetBaggageValue injects a key-value pair into the context's baggage. Keys and values must comply with W3C Baggage; invalid key/value returns a wrapped error.

Types

type Option added in v0.1.2

type Option func(*config)

Option configures New. Use WithServiceName, WithTraceRatio, etc.

func WithEnvironment added in v0.1.2

func WithEnvironment(env string) Option

WithEnvironment sets the deployment environment (e.g. "production", "staging").

func WithExporter added in v0.2.0

func WithExporter(exp sdktrace.SpanExporter) Option

WithExporter sets the span exporter. If not set, a no-op exporter is used.

func WithMetricExporter added in v0.1.2

func WithMetricExporter(exp sdkmetric.Exporter) Option

WithMetricExporter sets the metric exporter. If not set, metrics are not exported.

func WithSampler added in v0.2.5

func WithSampler(sampler sdktrace.Sampler) Option

WithSampler sets a custom head-based sampler for tracing. When provided, this sampler takes precedence over WithTraceRatio.

func WithServiceName added in v0.1.2

func WithServiceName(name string) Option

WithServiceName sets the service name (required).

func WithServiceVersion added in v0.1.2

func WithServiceVersion(version string) Option

WithServiceVersion sets the service version (optional).

func WithTraceRatio added in v0.1.2

func WithTraceRatio(ratio float64) Option

WithTraceRatio sets the fraction of traces to sample (1.0 = 100%, 0.0 = disable).

type Provider added in v0.2.5

type Provider struct {
	TracerProvider trace.TracerProvider
	MeterProvider  metric.MeterProvider
	Propagator     propagation.TextMapPropagator
	// contains filtered or unexported fields
}

Provider is the stateless runtime object created by New. It exposes OTel providers and propagator without mutating global state.

func New added in v0.2.5

func New(ctx context.Context, opts ...Option) (*Provider, error)

New creates a metry runtime provider. The returned Provider does not install any global OTel state.

func (*Provider) Shutdown added in v0.2.5

func (p *Provider) Shutdown(ctx context.Context) error

Shutdown releases resources owned by this Provider. Shutdown is idempotent.

Directories

Path Synopsis
Package genai defines GenAI semantic-convention constants and helpers for metry.
Package genai defines GenAI semantic-convention constants and helpers for metry.
middleware
http
Package http provides HTTP middleware for metry that creates root spans and propagates trace context.
Package http provides HTTP middleware for metry that creates root spans and propagates trace context.
Package security provides semantic conventions and helpers for AI security observability telemetry.
Package security provides semantic conventions and helpers for AI security observability telemetry.
Package testutil provides in-memory exporters and helpers for testing code that uses metry.
Package testutil provides in-memory exporters and helpers for testing code that uses metry.
Package traceutil provides utilities for OpenTelemetry trace spans.
Package traceutil provides utilities for OpenTelemetry trace spans.

Jump to

Keyboard shortcuts

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