otelchi

repository·master·Indexed 41 days ago

https://github.com/riandyrn/otelchi

OpenTelemetry instrumentation for the go-chi/chi router. It provides middleware to capture traces and metrics for HTTP servers following OpenTelemetry semantic conventions, including support for custom span naming, request filtering, and trace ID injection in response headers.

Tokens
2.7K
Snippets
13
Records
17
Agent score
51%

What's inside otelchi

  1. Access Observability UIs in the Multi Services Example

    master

    After running the multi-services example, you can visualize the collected observability data using the following interfaces:

    • Distributed Tracing: Access the Jaeger UI at http://localhost:16686 to view traces across the front-svc and back-svc services.
    • Metrics: Access the Prometheus UI at http://localhost:9090 to query and visualize metrics collected from both services.
  2. Run the Multi Services Example

    master

    The Multi Services Example demonstrates instrumenting multiple services (front-svc and back-svc) to show how distributed tracing and metrics flow through a system.

    To run the full environment, including the services, Jaeger (for tracing), and Prometheus (for metrics), ensure you have Docker and Docker Compose installed, then execute the following command:

    make run

    Once the services are running, you will see logs indicating that the services are listening on their respective ports (e.g., :8090 for front-svc and :8091 for back-svc).

  3. Run the go-chi/chi instrumentation example with Docker Compose

    master

    This example demonstrates an HTTP server using go-chi/chi with instrumentation. The server includes a /users/{id:[0-9]+} endpoint and outputs span information to stdout.

    To run the example, use Docker Compose to start both the mux-server (the instrumented server) and the mux-client (which triggers a single request).

    docker compose up --detach mux-server mux-client
  4. Handle Public Endpoints and Trace Linking

    master

    In microservice architectures, you may want to distinguish between 'public' entry points and 'internal' service calls to manage trace continuity.

    • WithPublicEndpoint(): Marks endpoints as public. When a request arrives with an existing span context, the middleware will create a new root span (a new trace) and link it to the incoming span context, rather than making the incoming span a parent. This is useful for the edge services of a system.
    • WithPublicEndpointFn(fn func(r *http.Request) bool): Provides more granular control. If the provided function returns true, the middleware treats the request as a public endpoint (creating a new root span and linking it). If it returns false, the middleware treats the incoming span context as the parent of the generated span.

    Mental Model:

    • Internal Service: Use default behavior (incoming span is parent). This results in a single continuous trace across services.
    • Public Gateway: Use WithPublicEndpoint. This results in separate traces for different systems that are explicitly linked, preventing a single massive trace from spanning across unrelated organizational boundaries.
    // Example: Only treat requests to /api/v1/login as public entry points
    middleware := otelchi.Middleware(
        otelchi.WithPublicEndpointFn(func(r *http.Request) bool {
            return r.URL.Path == "/api/v1/login"
        }),
    )
  5. Configure otelchi Middleware with Options

    master

    The Middleware function accepts variadic Option arguments to customize tracing behavior. While the specific option functions are not defined in this file, they are applied to a config struct during initialization. Common customization areas include:

    • Providing a custom TracerProvider.
    • Configuring custom propagators for context extraction.
    • Setting a custom traceIDResponseHeaderKey to include the Trace ID in response headers.
    • Defining a publicEndpointFn to treat certain requests as new root spans (useful for edge gateways).
    • Providing chiRoutes to allow the middleware to resolve route patterns earlier in the request lifecycle.
  6. Include Trace ID in HTTP response headers

    master

    You can configure the middleware to automatically inject the current Trace ID and sampling status into the HTTP response headers. This is useful for client-side debugging or correlating client logs with server traces.

    Note: The exact option name is not explicitly shown in this file, but the logic is driven by the traceIDResponseHeaderKey and traceIDResponseHeaderKey configuration fields within the middleware.

  7. HTTP server metrics in otelchi

    master

    The metric package provides middleware that implements OpenTelemetry semantic-convention compliant HTTP server metrics.

    Supported metrics:

    • http.server.request.duration
    • http.server.active_requests
    • http.server.request.body.size
    • http.server.response.body.size

    Note: Legacy metric middleware for request_duration_millis, requests_inflight, and response_size_bytes is available but deprecated.

  8. Customize Span Naming and Routing

    master

    By default, otelchi may name spans at the end of execution. You can influence span naming using these options:

    • WithChiRoutes(routes chi.Routes): Provides the chi routes to the middleware. This allows the middleware to use the route pattern (e.g., /users/{id}) as the span name during span creation, rather than the raw URL. This is useful if you want to allow underlying handlers to override the span name.
    • WithRequestMethodInSpanName(isActive bool): Adds the HTTP method (e.g., GET, POST) to the span name. This is recommended for vendors like Elastic or New Relic that may not automatically include the method in the span name.
    // Use chi routes for pattern-based span names
    opts := otelchi.WithChiRoutes(r.RouteContext(r.Context()))
    
    // Include HTTP method in span names
    opts := otelchi.WithRequestMethodInSpanName(true)
  9. Use the Middleware function to instrument chi routers

    master

    The Middleware function creates an OpenTelemetry tracing middleware for go-chi routers. It starts a new span for incoming requests, extracts tracing context from headers using the configured propagator, and automatically records HTTP attributes like status codes and route patterns.

    To use it, call otelchi.Middleware(serverName, opts...) where serverName is a string describing your server. You can pass optional configuration via functional options.

    By default, it uses the global OpenTelemetry TextMapPropagator and TracerProvider if none are provided via options.

    import (
    	"github.com/go-chi/chi/v5"
    	"github.com/riandyrn/otelchi"
    )
    
    r := chi.NewRouter()
    r.Use(otelchi.Middleware("my-server-name"))
    
    r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
    	w.Write([]byte("world"))
    })