### Basic otelchi Middleware Setup Source: https://context7.com/riandyrn/otelchi/llms.txt This example demonstrates setting up a TracerProvider with a stdout exporter and integrating the otelchi.Middleware into a chi router. Ensure Go 1.22+ and OpenTelemetry Go SDK v1.34.0+ are used. ```go package main import ( "net/http" "github.com/go-chi/chi/v5" "github.com/riandyrn/otelchi" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" ) func main() { // 1. Set up a TracerProvider (stdout exporter for illustration). exp, _ := stdout.New(stdout.WithPrettyPrint()) tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), sdktrace.WithSampler(sdktrace.AlwaysSample()), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator( propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, ), ) // 2. Create a chi router and attach the otelchi middleware. r := chi.NewRouter() r.Use( otelchi.Middleware( "my-server", // server name, set as net.host.name attribute otelchi.WithChiRoutes(r), // provide routes so span name is set at span creation ), ) r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello " + chi.URLParam(r, "id"))) }) // Spans are emitted to stdout on every request. // GET /users/42 → span name "GET /users/{id}" http.ListenAndServe(":8080", r) } ``` -------------------------------- ### Start Docker Compose Services Source: https://github.com/riandyrn/otelchi/blob/master/examples/basic/README.md Use this command to bring up the mux-server and mux-client services for the example. Ensure Docker Compose is installed. ```sh docker compose up --detach mux-server mux-client ``` -------------------------------- ### Run Multi Services Example Source: https://github.com/riandyrn/otelchi/blob/master/examples/multi-services/README.md Execute this command to build and run the multi-service example using Docker Compose. Ensure Docker and Docker Compose are installed. ```bash > make run ``` -------------------------------- ### Full Integration Example with Tracing and Metrics Source: https://context7.com/riandyrn/otelchi/llms.txt This example demonstrates the canonical pattern for using trace and all four metric middlewares together with a stdout exporter. It sets up OpenTelemetry providers for tracing and metrics, configures the otelchi middleware with various options, and defines routes for handling requests. Use this for local development and debugging. ```go package main import ( "context" "fmt" "log" "net/http" "github.com/go-chi/chi/v5" "github.com/riandyrn/otelchi" otelchimetric "github.com/riandyrn/otelchi/metric" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdkmetric "go.opentelemetry.io/otel/sdk/metric" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.20.0" oteltrace "go.opentelemetry.io/otel/trace" ) const serverName = "my-server" var tracer oteltrace.Tracer func main() { // --- Trace provider setup --- traceExp, _ := stdout.New(stdout.WithPrettyPrint()) res, _ := resource.New(context.Background(), resource.WithAttributes(semconv.ServiceNameKey.String(serverName)), ) tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(traceExp), sdktrace.WithResource(res), ) defer tp.Shutdown(context.Background()) otel.SetTracerProvider(tp) otel.SetTextMapPropagator( propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, ), ) tracer = otel.Tracer(serverName) // --- Metric provider setup --- metricExp, _ := stdoutmetric.New() mp := sdkmetric.NewMeterProvider( sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExp)), ) otel.SetMeterProvider(mp) // --- Metric base config --- baseCfg := otelchimetric.NewBaseConfig(serverName, otelchimetric.WithMeterProvider(mp), ) // --- Router + all middlewares --- r := chi.NewRouter() r.Use( otelchi.Middleware(serverName, otelchi.WithChiRoutes(r), otelchi.WithRequestMethodInSpanName(true), otelchi.WithTraceResponseHeaders(otelchi.TraceHeaderConfig{}), otelchi.WithFilter(func(r *http.Request) bool { return r.URL.Path != "/healthz" }), ), otelchimetric.NewServerRequestDuration(baseCfg), otelchimetric.NewServerActiveRequests(baseCfg), otelchimetric.NewServerRequestBodySize(baseCfg), otelchimetric.NewServerResponseBodySize(baseCfg), ) r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) // not traced, still measured }) r.Get("/users/{id:[0-9]+}", func(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") // Create a child span inside the handler. _, span := tracer.Start(r.Context(), "lookupUser", oteltrace.WithAttributes(attribute.String("user.id", id)), ) defer span.End() fmt.Fprintf(w, "user id=%s\n", id) }) log.Println("listening on :8080") http.ListenAndServe(":8080", r) } // Example request: // curl -v http://localhost:8080/users/42 // // Response headers include: // X-Trace-Id: // X-Trace-Sampled: true // // Stdout trace output (abbreviated): // { // "Name": "GET /users/{id:[0-9]+}", // "SpanContext": { "TraceID": "...", "SpanID": "..." }, // "Attributes": [ // { "Key": "http.method", "Value": "GET" }, // { "Key": "http.route", "Value": "/users/{id:[0-9]+}" }, // { "Key": "http.status_code", "Value": 200 } // ] // } ``` -------------------------------- ### Install otelchi Package Source: https://github.com/riandyrn/otelchi/blob/master/README.md Use this command to add the otelchi library to your Go project. ```bash go get github.com/riandyrn/otelchi ``` -------------------------------- ### Example Service Output Source: https://github.com/riandyrn/otelchi/blob/master/examples/multi-services/README.md Observe this output in the terminal to confirm that both services are running and listening on their respective ports. This indicates a successful startup. ```bash back-svc_1 | 2022/07/23 01:49:29 back service is listening on :8091 front-svc_1 | 2022/07/23 01:49:26 front service is listening on :8090 ... multi-services_client_1 exited with code 0 ``` -------------------------------- ### Shut Down Docker Compose Services Source: https://github.com/riandyrn/otelchi/blob/master/examples/basic/README.md Run this command to stop and remove the services once you are finished with the example. ```sh docker compose down ``` -------------------------------- ### Get otelchi Library Version Source: https://context7.com/riandyrn/otelchi/llms.txt Retrieves the current semantic version string of the otelchi library at runtime. This is used internally for setting instrumentation versions. ```go import "github.com/riandyrn/otelchi/version" fmt.Println(version.Version()) // Output: 0.12.3 ``` -------------------------------- ### Prepend HTTP Method to Span Names with otelchi.WithRequestMethodInSpanName Source: https://context7.com/riandyrn/otelchi/llms.txt Prepends the HTTP method to the span name (e.g., "GET /users/{id}" instead of "/users/{id}"). Required for vendors like Elastic APM and New Relic that do not parse the HTTP method from span attributes automatically. ```go r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithChiRoutes(r), otelchi.WithRequestMethodInSpanName(true), )) // Span names: "GET /users/{id}", "POST /orders", etc. ``` -------------------------------- ### Track Active HTTP Server Requests Source: https://context7.com/riandyrn/otelchi/llms.txt Tracks the number of in-flight requests using an Int64UpDownCounter. The counter increments at request start and decrements when the handler returns. Conforms to http.server.active_requests. Requires importing the otelchi metric package. ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerActiveRequests(baseCfg)) ``` -------------------------------- ### metric.NewServerActiveRequests Source: https://context7.com/riandyrn/otelchi/llms.txt Tracks the number of in-flight requests using an Int64UpDownCounter. The counter increments at request start and decrements when the handler returns. Conforms to http.server.active_requests. ```APIDOC ## `metric.NewServerActiveRequests` — `http.server.active_requests` ### Description Tracks the number of in-flight requests using an `Int64UpDownCounter`. The counter increments at request start and decrements when the handler returns. Conforms to `http.server.active_requests`. ### Usage ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerActiveRequests(baseCfg)) ``` ### Metric Details - **Name**: `http.server.active_requests` - **Unit**: `{request}` - **Type**: `UpDownCounter` - **Value**: Live count of concurrent requests ``` -------------------------------- ### Configure Public Gateway Tracing with otelchi.WithPublicEndpoint / WithPublicEndpointFn Source: https://context7.com/riandyrn/otelchi/llms.txt Marks the server as a public entry point. Instead of adopting an incoming remote span as the parent, the middleware creates a new root span and links it to the incoming span context. This separates traces cleanly between systems while maintaining the cross-system relationship. ```go import ( "net/http" "github.com/riandyrn/otelchi" ) // WithPublicEndpoint — treat every request as a new trace root. r := chi.NewRouter() r.Use(otelchi.Middleware("public-gateway", otelchi.WithPublicEndpoint())) // WithPublicEndpointFn — selectively treat only certain requests as public. r2 := chi.NewRouter() r2.Use(otelchi.Middleware("mixed-gateway", otelchi.WithPublicEndpointFn(func(r *http.Request) bool { // Only requests coming from external IPs are treated as new roots. return r.Header.Get("X-Internal") == "" }), )) ``` -------------------------------- ### Initialize Metrics Middleware Configuration with metric.NewBaseConfig Source: https://context7.com/riandyrn/otelchi/llms.txt `NewBaseConfig` initialises the shared configuration for all metric middlewares. It creates the OTel `Meter` scoped to "github.com/riandyrn/otelchi/metric" and sets default attributes (`http.method`, `http.scheme`, `http.route`). A custom `WithAttributesFunc` can replace the default attribute set entirely. ```go import ( otelchimetric "github.com/riandyrn/otelchi/metric" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" ) // Build a MeterProvider. exp, _ := stdoutmetric.New() mp := sdkmetric.NewMeterProvider( sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exp)), ) // Create base config with a custom attribute function. baseCfg := otelchimetric.NewBaseConfig( "my-server", otelchimetric.WithMeterProvider(mp), otelchimetric.WithAttributesFunc(func(r *http.Request) []attribute.KeyValue { return []attribute.KeyValue{ attribute.String("http.method", r.Method), attribute.String("http.route", chi.RouteContext(r.Context()).RoutePattern()), attribute.String("tenant", r.Header.Get("X-Tenant-ID")), } }), ) ``` -------------------------------- ### metric.NewBaseConfig Source: https://context7.com/riandyrn/otelchi/llms.txt Initializes shared configuration for metric middlewares, creating an OTel Meter and setting default attributes. A custom `WithAttributesFunc` can be provided to override these defaults. ```APIDOC ## `metric.NewBaseConfig` — Metrics Middleware Configuration `NewBaseConfig` initialises the shared configuration for all metric middlewares. It creates the OTel `Meter` scoped to `"github.com/riandyrn/otelchi/metric"` and sets default attributes (`http.method`, `http.scheme`, `http.route`). A custom `WithAttributesFunc` can replace the default attribute set entirely. ```go import ( otelchimetric "github.com/riandyrn/otelchi/metric" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" ) // Build a MeterProvider. exp, _ := stdoutmetric.New() mp := sdkmetric.NewMeterProvider( sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exp)), ) // Create base config with a custom attribute function. baseCfg := otelchimetric.NewBaseConfig( "my-server", otelchimetric.WithMeterProvider(mp), otelchimetric.WithAttributesFunc(func(r *http.Request) []attribute.KeyValue { return []attribute.KeyValue{ attribute.String("http.method", r.Method), attribute.String("http.route", chi.RouteContext(r.Context()).RoutePattern()), attribute.String("tenant", r.Header.Get("X-Tenant-ID")), } }), ) ``` ``` -------------------------------- ### otelchi.WithPublicEndpoint / WithPublicEndpointFn Source: https://context7.com/riandyrn/otelchi/llms.txt Marks the server as a public entry point, creating a new root span for incoming requests and linking it to the remote span context to cleanly separate traces between systems. ```APIDOC ## `otelchi.WithPublicEndpoint` / `WithPublicEndpointFn` — Public Gateway Tracing Marks the server as a public entry point. Instead of adopting an incoming remote span as the parent, the middleware creates a new root span and **links** it to the incoming span context. This separates traces cleanly between systems while maintaining the cross-system relationship. ```go import ( "net/http" "github.com/riandyrn/otelchi" ) // WithPublicEndpoint — treat every request as a new trace root. r := chi.NewRouter() r.Use(otelchi.Middleware("public-gateway", otelchi.WithPublicEndpoint())) // WithPublicEndpointFn — selectively treat only certain requests as public. r2 := chi.NewRouter() r2.Use(otelchi.Middleware("mixed-gateway", otelchi.WithPublicEndpointFn(func(r *http.Request) bool { // Only requests coming from external IPs are treated as new roots. return r.Header.Get("X-Internal") == "" }), )) ``` ``` -------------------------------- ### Inject Custom TracerProvider with WithTracerProvider Source: https://context7.com/riandyrn/otelchi/llms.txt Use otelchi.WithTracerProvider to inject a specific TracerProvider, which is useful for testing or managing multiple independent OpenTelemetry pipelines within a single process. ```go import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" "github.com/riandyrn/otelchi" ) tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample())) r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithTracerProvider(tp))) ``` -------------------------------- ### Configure Custom Propagators with otelchi.WithPropagators Source: https://context7.com/riandyrn/otelchi/llms.txt Override the global text-map propagators. Useful when a service uses a non-standard propagation format alongside W3C TraceContext. ```go import ( "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/contrib/propagators/b3" ) propagator := propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)), ) r := chi.NewRouter() r.Use(otelchi.Middleware("svc", otelchi.WithPropagators(propagator))) ``` -------------------------------- ### Record HTTP Server Request Body Size Source: https://context7.com/riandyrn/otelchi/llms.txt Wraps r.Body in a counting reader that tallies bytes consumed by handlers, then records the total as an Int64Histogram in bytes. Only non-nil, non-http.NoBody request bodies are instrumented. Requires importing the otelchi metric package. ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerRequestBodySize(baseCfg)) ``` -------------------------------- ### Filter Requests with otelchi.WithFilter Source: https://context7.com/riandyrn/otelchi/llms.txt Attach one or more Filter predicates. A request is only traced when all filters return true. Use this to suppress tracing for health checks, metrics scrape endpoints, or static assets. ```go import ( "net/http" "strings" "github.com/riandyrn/otelchi" ) // Skip tracing for /healthz and Prometheus scrape paths. skipHealth := otelchi.Filter(func(r *http.Request) bool { return r.URL.Path != "/healthz" }) skipMetrics := otelchi.Filter(func(r *http.Request) bool { return !strings.HasPrefix(r.URL.Path, "/metrics") }) r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithFilter(skipHealth), otelchi.WithFilter(skipMetrics), )) r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) // Not traced. }) r.Get("/data", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("traced")) // Traced normally. }) ``` -------------------------------- ### Record HTTP Server Response Body Size Source: https://context7.com/riandyrn/otelchi/llms.txt Wraps the http.ResponseWriter to count bytes written to the response, then records the total as an Int64Histogram in bytes. Requires importing the otelchi metric package. ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerResponseBodySize(baseCfg)) ``` -------------------------------- ### Enable Early Span Naming with WithChiRoutes Source: https://context7.com/riandyrn/otelchi/llms.txt Use otelchi.WithChiRoutes to pre-resolve chi route patterns for immediate span naming. This allows downstream handlers to override the span name if needed. ```go r := chi.NewRouter() // Pass r to WithChiRoutes so the middleware can call r.Match() // before handler execution, resolving "/items/{sku}" immediately. r.Use(otelchi.Middleware("shop-api", otelchi.WithChiRoutes(r))) r.Get("/items/{sku}", func(w http.ResponseWriter, r *http.Request) { // Span is already named "GET /items/{sku}" here. // You may override it: span := trace.SpanFromContext(r.Context()) span.SetName("custom-span-name") w.WriteHeader(http.StatusOK) }) ``` -------------------------------- ### Deprecated Legacy Metric Middlewares Source: https://context7.com/riandyrn/otelchi/llms.txt These functions are deprecated and superseded by semantic-convention-compliant counterparts. They are kept for backward compatibility only. Prefer using `NewServerRequestDuration`, `NewServerActiveRequests`, and `NewServerResponseBodySize`. ```go // Old (deprecated) — kept for backward compatibility only. r.Use( otelchimetric.NewRequestDurationMillis(baseCfg), // metric: request_duration_millis (ms) otelchimetric.NewRequestInFlight(baseCfg), // metric: requests_inflight otelchimetric.NewResponseSizeBytes(baseCfg), // metric: response_size_bytes ) // Prefer the new equivalents: r.Use( otelchimetric.NewServerRequestDuration(baseCfg), // http.server.request.duration (s) otelchimetric.NewServerActiveRequests(baseCfg), // http.server.active_requests otelchimetric.NewServerResponseBodySize(baseCfg), // http.server.response.body.size ) ``` -------------------------------- ### View Server Logs for Spans Source: https://github.com/riandyrn/otelchi/blob/master/examples/basic/README.md After the services are running, use this command to view the logs from the mux-server, which will contain the generated span information. ```sh docker compose logs mux-server ``` -------------------------------- ### Inject Trace ID in Response Headers with otelchi.WithTraceResponseHeaders Source: https://context7.com/riandyrn/otelchi/llms.txt Injects the active trace ID and sampling decision into HTTP response headers, enabling clients to correlate their requests with backend traces. ```go import "github.com/riandyrn/otelchi" r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithTraceResponseHeaders(otelchi.TraceHeaderConfig{ TraceIDHeader: "X-Trace-Id", // default TraceSampledHeader: "X-Trace-Sampled", // default }), )) // Response headers will contain: // X-Trace-Id: 4bf92f3577b34da6a3ce929d0e0e4736 // X-Trace-Sampled: true ``` -------------------------------- ### otelchi.WithPropagators Source: https://context7.com/riandyrn/otelchi/llms.txt Allows overriding the global text-map propagators. This is useful for services that need to support non-standard propagation formats alongside W3C TraceContext. ```APIDOC ## `otelchi.WithPropagators` — Custom Propagators Override the global text-map propagators. Useful when a service uses a non-standard propagation format (e.g., B3 multi-header) alongside W3C TraceContext. ```go import ( "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/contrib/propagators/b3" ) propagator := propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)), ) r := chi.NewRouter() r.Use(otelchi.Middleware("svc", otelchi.WithPropagators(propagator))) ``` ``` -------------------------------- ### Record HTTP Server Request Duration Source: https://context7.com/riandyrn/otelchi/llms.txt Records the end-to-end duration of each HTTP request as a Float64Histogram in seconds. Conforms to the http.server.request.duration semantic convention. Requires importing the otelchi metric package. ```go import ( "github.com/go-chi/chi/v5" otelchimetric "github.com/riandyrn/otelchi/metric" ) r := chi.NewRouter() r.Use(otelchimetric.NewServerRequestDuration(baseCfg)) ``` -------------------------------- ### otelchi.WithRequestMethodInSpanName Source: https://context7.com/riandyrn/otelchi/llms.txt Prepends the HTTP method to the span name, which is useful for tracing systems that do not automatically parse the HTTP method from span attributes. ```APIDOC ## `otelchi.WithRequestMethodInSpanName` — Method Prefix in Span Names Prepends the HTTP method to the span name (e.g., `"GET /users/{id}"` instead of `"/users/{id}"`). Required for vendors like Elastic APM and New Relic that do not parse the HTTP method from span attributes automatically. ```go r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithChiRoutes(r), otelchi.WithRequestMethodInSpanName(true), )) // Span names: "GET /users/{id}", "POST /orders", etc. ``` ``` -------------------------------- ### metric.NewServerRequestBodySize Source: https://context7.com/riandyrn/otelchi/llms.txt Wraps r.Body in a counting reader that tallies bytes consumed by handlers, then records the total as an Int64Histogram in bytes. Only non-nil, non-http.NoBody request bodies are instrumented. ```APIDOC ## `metric.NewServerRequestBodySize` — `http.server.request.body.size` ### Description Wraps `r.Body` in a counting reader that tallies bytes consumed by handlers, then records the total as an `Int64Histogram` in **bytes**. Only non-nil, non-`http.NoBody` request bodies are instrumented. ### Usage ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerRequestBodySize(baseCfg)) ``` ### Metric Details - **Name**: `http.server.request.body.size` - **Unit**: `By` - **Type**: `Histogram` - **Value**: Bytes read from request body per request ``` -------------------------------- ### otelchi.WithTraceResponseHeaders Source: https://context7.com/riandyrn/otelchi/llms.txt Injects the active trace ID and sampling decision into HTTP response headers, allowing clients to correlate their requests with backend traces. ```APIDOC ## `otelchi.WithTraceResponseHeaders` — Trace ID in Response Headers Injects the active trace ID and sampling decision into HTTP response headers, enabling clients to correlate their requests with backend traces. ```go import "github.com/riandyrn/otelchi" r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithTraceResponseHeaders(otelchi.TraceHeaderConfig{ TraceIDHeader: "X-Trace-Id", // default TraceSampledHeader: "X-Trace-Sampled", // default }), )) // Response headers will contain: // X-Trace-Id: 4bf92f3577b34da6a3ce929d0e0e4736 // X-Trace-Sampled: true ``` ``` -------------------------------- ### metric.NewServerResponseBodySize Source: https://context7.com/riandyrn/otelchi/llms.txt Wraps the http.ResponseWriter to count bytes written to the response, then records the total as an Int64Histogram in bytes. ```APIDOC ## `metric.NewServerResponseBodySize` — `http.server.response.body.size` ### Description Wraps the `http.ResponseWriter` to count bytes written to the response, then records the total as an `Int64Histogram` in **bytes**. ### Usage ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerResponseBodySize(baseCfg)) ``` ### Metric Details - **Name**: `http.server.response.body.size` - **Unit**: `By` - **Type**: `Histogram` - **Value**: Bytes written to response body per request ``` -------------------------------- ### otelchi.WithFilter Source: https://context7.com/riandyrn/otelchi/llms.txt Enables request filtering by attaching one or more `Filter` predicates. A request is only traced if all provided filters return `true`, allowing suppression of tracing for specific endpoints like health checks. ```APIDOC ## `otelchi.WithFilter` — Request Filtering Attach one or more `Filter` predicates (`func(*http.Request) bool`). A request is only traced when **all** filters return `true`. Use this to suppress tracing for health checks, metrics scrape endpoints, or static assets. ```go import ( "net/http" "strings" "github.com/riandyrn/otelchi" ) // Skip tracing for /healthz and Prometheus scrape paths. skipHealth := otelchi.Filter(func(r *http.Request) bool { return r.URL.Path != "/healthz" }) skipMetrics := otelchi.Filter(func(r *http.Request) bool { return !strings.HasPrefix(r.URL.Path, "/metrics") }) r := chi.NewRouter() r.Use(otelchi.Middleware("api", otelchi.WithFilter(skipHealth), otelchi.WithFilter(skipMetrics), )) r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) // Not traced. }) r.Get("/data", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("traced")) // Traced normally. }) ``` ``` -------------------------------- ### metric.NewServerRequestDuration Source: https://context7.com/riandyrn/otelchi/llms.txt Records the end-to-end duration of each HTTP request as a Float64Histogram in seconds, using standard OTel bucket boundaries. Conforms to the http.server.request.duration semantic convention. ```APIDOC ## `metric.NewServerRequestDuration` — `http.server.request.duration` ### Description Records the end-to-end duration of each HTTP request as a `Float64Histogram` in **seconds**, using the standard OTel bucket boundaries (`0.005s … 10s`). Conforms to the `http.server.request.duration` semantic convention. ### Usage ```go r := chi.NewRouter() r.Use(otelchimetric.NewServerRequestDuration(baseCfg)) ``` ### Metric Details - **Name**: `http.server.request.duration` - **Unit**: `s` - **Type**: `Histogram` (explicit boundaries: 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10) - **Attributes**: `http.method`, `http.scheme`, `http.route` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.