README
¶
montaur-trace-go
pixelbuf's Go tracing glue for apps that ship traces to a node-local
montaur-agent (an OpenTelemetry Collector). It is pure glue around
upstream go.opentelemetry.io modules — no custom protocol code — so the
API surface is tiny and the dependency graph stays minimal. Think of this as
montaur's analog to dd-trace-go.
Apps talk to the agent over plain HTTP on the local node; the agent holds the montaur API key and forwards traces upstream. No credentials are configured in this library or in the app.
Quickstart
import "github.com/pixelbuf/montaur-trace-go/montaurtrace"
shutdown, err := montaurtrace.Init(ctx, montaurtrace.Config{Service: "checkout"})
if err != nil {
log.Fatal(err)
}
defer shutdown(ctx)
http.ListenAndServe(":8080", montaurtrace.HTTP(mux))
That's it: Init wires up the global OpenTelemetry TracerProvider and W3C
propagators, and montaurtrace.HTTP instruments your server. Wrap outbound
http.Clients with montaurtrace.Transport and gRPC servers/clients with
montaurtrace.GRPCServer/montaurtrace.GRPCClient for end-to-end trace
propagation.
If you name spans yourself, keep the names low-cardinality — route
patterns like GET /users/:id, never raw URLs or IDs — because span.name
becomes a montaur tag key, and montaur caps each tag key at 500 distinct
values per tenant.
Kubernetes: you MUST set the endpoint
The default endpoint is http://localhost:4318, which suits VM/systemd
co-location only (where the montaur-agent container publishes its OTLP
ports on the same host). From a Kubernetes app pod it is
connection-refused: the montaur-agent DaemonSet exposes no hostPort, so
apps must reach the agent through its node-local ClusterIP Service. Set
OTEL_EXPORTER_OTLP_ENDPOINT (or Config.Endpoint) to the montaur-agent
Service URL in your application Deployment's pod spec, mirroring the
montaur repo's agent/k8s/daemonset.yaml wiring:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://montaur-agent-prod.CHANGEME-namespace.svc.cluster.local:4318
(4318 = OTLP/HTTP, which is what this library exports over.)
HTTP client propagation
client := &http.Client{Transport: montaurtrace.Transport(nil)}
gRPC
srv := grpc.NewServer(grpc.StatsHandler(montaurtrace.GRPCServer()))
conn, err := grpc.NewClient(target, grpc.WithStatsHandler(montaurtrace.GRPCClient()))
Filtering
HTTP, GRPCServer, and GRPCClient accept the underlying
otelhttp/otelgrpc options variadically. Two helpers cover the common
case — keeping health checks out of the traces — without making the app
import the contrib packages:
srv := grpc.NewServer(grpc.StatsHandler(montaurtrace.GRPCServer(
montaurtrace.WithoutGRPCMethods("/grpc.health.v1.Health/Check"),
)))
handler := montaurtrace.HTTP(mux, montaurtrace.WithoutHTTPPaths("/health"))
Both match exactly: WithoutGRPCMethods on the full RPC method string
(/package.Service/Method), WithoutHTTPPaths on r.URL.Path. Filtered
requests are served normally, just without a span (and, for gRPC, without
trace-context propagation).
Config
type Config struct {
Service string // required; becomes resource attribute service.name
Version string // optional; service.version
Environment string // optional; deployment.environment
Endpoint string // optional; overrides OTEL_EXPORTER_OTLP_TRACES_ENDPOINT and OTEL_EXPORTER_OTLP_ENDPOINT
SampleRatio *float64 // optional; overrides OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG; default 1.0
}
Service is the only required field; Init returns an error if it is
empty.
Environment variable contract
| Variable | Effect | Precedence |
|---|---|---|
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
Traces-specific base URL of the OTLP/HTTP receiver. | Config.Endpoint > this > OTEL_EXPORTER_OTLP_ENDPOINT > http://localhost:4318 |
OTEL_EXPORTER_OTLP_ENDPOINT |
Base URL of the OTLP/HTTP receiver, e.g. http://montaur-agent:4318. |
Config.Endpoint > traces env > this > http://localhost:4318 |
OTEL_TRACES_SAMPLER |
Standard SDK sampler selection (e.g. always_off, traceidratio). |
Honored only when Config.SampleRatio is nil: Init then installs no sampler and defers to the SDK's own env handling of the OTEL_TRACES_SAMPLER/OTEL_TRACES_SAMPLER_ARG pair. |
OTEL_TRACES_SAMPLER_ARG |
Head sampling ratio in [0, 1] for ParentBased(TraceIDRatioBased(...)) when OTEL_TRACES_SAMPLER is unset. |
Config.SampleRatio > env > 1.0 |
Notes:
Config.Endpoint/the endpoint env vars are a full base URL (scheme + host + port).http://is allowed and expected for talking to the cluster-local agent — no TLS, no auth headers.- Sampler rule: an explicit
Config.SampleRatioalways wins, over both sampler env vars. WithConfig.SampleRationil andOTEL_TRACES_SAMPLERset (even to something this library never installs itself, likealways_off), the SDK's env machinery is in full control. With both unset,ParentBased(TraceIDRatioBased)is installed, driven byOTEL_TRACES_SAMPLER_ARGalone. - An out-of-range sample ratio (from either
Configor the environment) is clamped to[0, 1]. A malformedOTEL_TRACES_SAMPLER_ARG(fails to parse as a float) is treated as unset and falls back to1.0(sample everything) rather than silently under-sampling. - No auth/API-key environment variables are read or required by this library. The app never talks to montaur directly — only to the local agent, which is where the montaur API key lives.
Endpoint semantics
These deviate slightly from stock OTel SDK env handling (we resolve the
endpoint ourselves and hand a single URL to
otlptracehttp.WithEndpointURL, which uses the URL's path as-is):
- Resolution order:
Config.Endpoint>OTEL_EXPORTER_OTLP_TRACES_ENDPOINT>OTEL_EXPORTER_OTLP_ENDPOINT>http://localhost:4318. - Trailing-slash normalization: a URL whose path is empty or exactly
/is stripped to no path, so the exporter's own/v1/tracesdefault applies. (http://agent:4318andhttp://agent:4318/both export tohttp://agent:4318/v1/traces.) - Custom paths are kept verbatim and are NOT suffixed with
/v1/traces— a custom path must be the complete traces endpoint path, e.g.http://proxy:8080/otlp/v1/traces. This differs from stock OTel handling ofOTEL_EXPORTER_OTLP_ENDPOINT, where the generic endpoint's path gets/v1/tracesappended per signal.
Calling Init more than once
Unsupported. Init sets process-global OpenTelemetry state (the
TracerProvider and TextMapPropagator); a second call overwrites the
first ("last write wins"). Call it once, early in process startup, and
defer shutdown(ctx) at the same call site.
Dependencies
Only go.opentelemetry.io/* modules (plus google.golang.org/grpc, pulled
in transitively by otelgrpc's stats handler). See go.mod for exact
versions.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package montaurtrace is pixelbuf's Go tracing glue for shipping traces to a node-local montaur-agent (an OpenTelemetry Collector).
|
Package montaurtrace is pixelbuf's Go tracing glue for shipping traces to a node-local montaur-agent (an OpenTelemetry Collector). |