Documentation
¶
Overview ¶
Package telemetry builds forge's OpenTelemetry tracing: config resolution, the shared resource, the sampler, the tracer provider and the HTTP server middleware.
Index ¶
- Constants
- Variables
- func BuildResource(ctx context.Context, name, version, environment string, ...) (*resource.Resource, error)
- func BuildSampler(s Settings) sdktrace.Sampler
- func Excluded(path string, prefixes []string) bool
- func HTTPClient() *http.Client
- func InstrumentTransport(rt http.RoundTripper) http.RoundTripper
- func NewForgeSampler(inner sdktrace.Sampler, off bool, wants *atomic.Pointer[[]func() bool]) sdktrace.Sampler
- func NormalizeMethod(m string) (method, original string)
- func PropagationMiddleware() func(http.Handler) http.Handler
- func RouteMatchMiddleware(excludePaths []string) func(http.Handler) http.Handler
- func SamplerOff(s Settings) bool
- func ServerMiddleware(tp trace.TracerProvider, s Settings) func(http.Handler) http.Handler
- type Inputs
- type MetricsInputs
- type MetricsPusher
- type MetricsSettings
- type Provider
- type Settings
- type StatusRecorder
- func (r *StatusRecorder) BytesWritten() int64
- func (r *StatusRecorder) Flush()
- func (r *StatusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error)
- func (r *StatusRecorder) Status() int
- func (r *StatusRecorder) Unwrap() http.ResponseWriter
- func (r *StatusRecorder) Write(b []byte) (int, error)
- func (r *StatusRecorder) WriteHeader(code int)
Constants ¶
const ( TemporalityCumulative = "cumulative" TemporalityDelta = "delta" )
Temporality values MetricsSettings carries.
const LifecycleKey = attribute.Key("forge.lifecycle")
LifecycleKey marks the root span of an app's boot or shutdown. The forge sampler always samples such a root unless sampling is turned off, because a boot happens once per process and the slow one is the one you need.
Variables ¶
var ErrInvalidEndpoint = errors.New("telemetry: invalid OTLP endpoint")
ErrInvalidEndpoint is wrapped by NewProvider when the OTLP endpoint is not an http or https URL with a host.
var ErrTracingDisabled = errors.New("telemetry: tracing is disabled")
ErrTracingDisabled is returned by RegisterSpanProcessor when tracing is off.
Functions ¶
func BuildResource ¶
func BuildResource(ctx context.Context, name, version, environment string, extra map[string]string) (*resource.Resource, error)
BuildResource builds the one Resource every signal shares. Order matters: later sources override earlier ones, so the app's name is the fallback, OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES beat it, and attributes from forge config beat everything.
A detector failing returns a partial resource with resource.ErrPartialResource. That is not fatal; the caller logs it and carries on with what was detected.
func BuildSampler ¶
BuildSampler maps a resolved sampler name to an SDK sampler.
func Excluded ¶
Excluded reports whether path starts with any of prefixes. The tracing and HTTP metrics middleware use it to skip paths such as "/_/".
func HTTPClient ¶
HTTPClient returns a client whose requests are traced. Every client shares http.DefaultTransport's connection pool.
func InstrumentTransport ¶
func InstrumentTransport(rt http.RoundTripper) http.RoundTripper
InstrumentTransport wraps rt so outbound requests get a client span and carry the trace headers. It captures the global tracer provider and propagator current at the call, so in a process with several apps the transport stays with whichever provider was current then. Build it once and keep the result.
func NewForgeSampler ¶
func NewForgeSampler(inner sdktrace.Sampler, off bool, wants *atomic.Pointer[[]func() bool]) sdktrace.Sampler
NewForgeSampler wraps inner. wants holds the WantsUnsampled functions of the registered processors; the provider owns it and replaces it on register.
func NormalizeMethod ¶
NormalizeMethod returns the semconv method value and, for a method outside the standard nine, the original. Arbitrary methods would otherwise create one span name and one metric series per value a client invents.
func PropagationMiddleware ¶
PropagationMiddleware only extracts the incoming trace context, so a traceparent still reaches outbound calls made with HTTPClient when tracing is turned off. It starts no span and writes no response header.
func RouteMatchMiddleware ¶
RouteMatchMiddleware only installs a fresh shared.RouteMatch holder, so the router can report the matched route to the stream observer. forge.New uses it when metrics are on but neither the tracing nor the HTTP metrics middleware is installed, since those install the holder otherwise. A path under excludePaths gets no holder, the same as the tracing and HTTP metrics middleware skip it, so a stream served there is not recorded.
func SamplerOff ¶
SamplerOff reports whether the settings turn sampling off entirely.
func ServerMiddleware ¶
ServerMiddleware starts a server span for every request whose path is not excluded. The span is renamed to "METHOD /route/pattern" once the router has matched, which it reports through shared.RouteMatch.
Types ¶
type Inputs ¶
type Inputs struct {
Code shared.TracingConfig // from WithAppTracingConfig
File shared.TracingConfig // from the forge config "tracing" key
Environment string
Getenv func(string) string
}
Inputs is everything Resolve reads.
type MetricsInputs ¶
type MetricsInputs struct {
Code shared.MetricsExportConfig // from WithAppMetricsExportConfig
File shared.MetricsExportConfig // from the forge config "metrics_export" key
Getenv func(string) string
}
MetricsInputs is everything ResolveMetrics reads.
type MetricsPusher ¶
type MetricsPusher struct {
// contains filtered or unexported fields
}
MetricsPusher pushes metrics over OTLP/HTTP on an interval between Start and Shutdown. It can be started again after a Shutdown, and the same producer serves every run.
func NewMetricsPusher ¶
func NewMetricsPusher(s MetricsSettings, res *resource.Resource, producer sdkmetric.Producer, log logger.Logger) (*MetricsPusher, error)
NewMetricsPusher checks the endpoint and keeps what Start needs. Nothing is exported until Start.
func (*MetricsPusher) Settings ¶
func (p *MetricsPusher) Settings() MetricsSettings
Settings returns the resolved settings the pusher was built with.
func (*MetricsPusher) Shutdown ¶
func (p *MetricsPusher) Shutdown(ctx context.Context) error
Shutdown collects and exports one last time and stops pushing, waiting at most five seconds or until ctx is done. It does nothing when the pusher isn't running, and Start may be called again afterwards.
func (*MetricsPusher) Start ¶
func (p *MetricsPusher) Start(ctx context.Context) error
Start builds the OTLP exporter and a periodic reader over the producer, and pushes every interval until Shutdown. A pusher that is already running is left as it is. The MeterProvider that holds them is never installed as the OTel global, so only what the producer returns is pushed and a push always carries the series a scrape shows.
otlpmetrichttp reads its own OTEL_EXPORTER_OTLP_METRICS_* settings for what is not passed here, such as the timeout, compression and TLS certificates.
type MetricsSettings ¶
type MetricsSettings struct {
Enabled bool // push runs
Endpoint string // full OTLP/HTTP metrics URL
Headers map[string]string
Interval time.Duration
Temporality string // TemporalityCumulative or TemporalityDelta
Warnings []string
}
MetricsSettings is the resolved metrics push configuration.
func ResolveMetrics ¶
func ResolveMetrics(in MetricsInputs) MetricsSettings
ResolveMetrics applies the precedence code, forge config, OTEL_* env, default. Push runs only when an endpoint resolves and nothing turned it off.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider owns the app's tracer provider.
func NewProvider ¶
func NewProvider(ctx context.Context, s Settings, res *resource.Resource, log logger.Logger) (*Provider, error)
NewProvider builds the tracer provider and installs it, with the propagators, as the OTel globals. With no endpoint the provider records spans but exports nothing; processors added later still see them.
func (*Provider) RegisterSpanProcessor ¶
func (p *Provider) RegisterSpanProcessor(sp sdktrace.SpanProcessor) error
RegisterSpanProcessor adds a span processor after construction. A processor with a WantsUnsampled method also receives spans the sampler would drop, for as long as the method returns true; those spans are never exported. It returns ErrTracingDisabled when tracing is off.
func (*Provider) Shutdown ¶
Shutdown flushes queued spans, waiting at most five seconds or until ctx is done.
func (*Provider) TracerProvider ¶
func (p *Provider) TracerProvider() trace.TracerProvider
TracerProvider returns the provider spans should be started from.
type Settings ¶
type Settings struct {
Enabled bool
Endpoint string // full OTLP/HTTP traces URL; empty means no export
Headers map[string]string
SamplerName string
SampleRatio float64
Propagators []string
ExcludePaths []string
ResponseHeader string
// SpanLabels labels each sampled request's goroutine with span_id and
// span_name for the CPU profiler. Resolve never sets it; forge.New copies
// it from the profiling settings.
SpanLabels bool
Resource map[string]string
Warnings []string
}
Settings is the resolved tracing configuration.
type StatusRecorder ¶
type StatusRecorder struct {
http.ResponseWriter
// contains filtered or unexported fields
}
StatusRecorder captures the response status and body size without hiding the optional interfaces SSE and WebSocket handlers need. The tracing middleware and the HTTP server metrics middleware both wrap the writer in one.
func NewStatusRecorder ¶
func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder
NewStatusRecorder wraps w.
func (*StatusRecorder) BytesWritten ¶
func (r *StatusRecorder) BytesWritten() int64
BytesWritten returns the response body bytes written so far.
func (*StatusRecorder) Flush ¶
func (r *StatusRecorder) Flush()
Flush flushes through http.ResponseController, so a writer below that only exposes Unwrap still gets flushed. A successful flush sends the headers, so it commits 200 when no status was written yet.
func (*StatusRecorder) Hijack ¶
func (r *StatusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error)
Hijack hijacks through http.ResponseController. It records 101 only when the hijack succeeded. When no writer below supports it, the error wraps http.ErrNotSupported.
func (*StatusRecorder) Status ¶
func (r *StatusRecorder) Status() int
Status returns the status sent, or 200 if the handler wrote nothing.
func (*StatusRecorder) Unwrap ¶
func (r *StatusRecorder) Unwrap() http.ResponseWriter
Unwrap lets http.ResponseController reach the underlying writer.
func (*StatusRecorder) WriteHeader ¶
func (r *StatusRecorder) WriteHeader(code int)
WriteHeader records the first final status and forwards every call. A 1xx informational response such as 103 Early Hints is passed on but not recorded, because the handler sends the real status after it. 101 is the exception: it ends the HTTP exchange, so it is final.