2022-03-08 14:18:32 -05:00
package tracing
import (
"fmt"
"net/http"
2023-08-14 10:41:15 -05:00
"go.uber.org/zap"
2022-03-08 14:18:32 -05:00
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func init ( ) {
caddy . RegisterModule ( Tracing { } )
httpcaddyfile . RegisterHandlerDirective ( "tracing" , parseCaddyfile )
}
// Tracing implements an HTTP handler that adds support for distributed tracing,
// using OpenTelemetry. This module is responsible for the injection and
// propagation of the trace context. Configure this module via environment
// variables (see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md).
// Some values can be overwritten in the configuration file.
type Tracing struct {
// SpanName is a span name. It should follow the naming guidelines here:
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span
SpanName string ` json:"span" `
// otel implements opentelemetry related logic.
otel openTelemetryWrapper
logger * zap . Logger
}
// CaddyModule returns the Caddy module information.
func ( Tracing ) CaddyModule ( ) caddy . ModuleInfo {
return caddy . ModuleInfo {
ID : "http.handlers.tracing" ,
New : func ( ) caddy . Module { return new ( Tracing ) } ,
}
}
// Provision implements caddy.Provisioner.
func ( ot * Tracing ) Provision ( ctx caddy . Context ) error {
2022-09-16 17:55:30 -05:00
ot . logger = ctx . Logger ( )
2022-03-08 14:18:32 -05:00
var err error
ot . otel , err = newOpenTelemetryWrapper ( ctx , ot . SpanName )
return err
}
// Cleanup implements caddy.CleanerUpper and closes any idle connections. It
// calls Shutdown method for a trace provider https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#shutdown.
func ( ot * Tracing ) Cleanup ( ) error {
if err := ot . otel . cleanup ( ot . logger ) ; err != nil {
return fmt . Errorf ( "tracerProvider shutdown: %w" , err )
}
return nil
}
// ServeHTTP implements caddyhttp.MiddlewareHandler.
func ( ot * Tracing ) ServeHTTP ( w http . ResponseWriter , r * http . Request , next caddyhttp . Handler ) error {
return ot . otel . ServeHTTP ( w , r , next )
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
//
2022-09-16 17:55:30 -05:00
// tracing {
// [span <span_name>]
// }
2022-03-08 14:18:32 -05:00
func ( ot * Tracing ) UnmarshalCaddyfile ( d * caddyfile . Dispenser ) error {
setParameter := func ( d * caddyfile . Dispenser , val * string ) error {
if d . NextArg ( ) {
* val = d . Val ( )
} else {
return d . ArgErr ( )
}
if d . NextArg ( ) {
return d . ArgErr ( )
}
return nil
}
// paramsMap is a mapping between "string" parameter from the Caddyfile and its destination within the module
paramsMap := map [ string ] * string {
"span" : & ot . SpanName ,
}
2024-01-23 19:36:59 -05:00
d . Next ( ) // consume directive name
if d . NextArg ( ) {
return d . ArgErr ( )
}
2022-03-08 14:18:32 -05:00
2024-01-23 19:36:59 -05:00
for d . NextBlock ( 0 ) {
if dst , ok := paramsMap [ d . Val ( ) ] ; ok {
if err := setParameter ( d , dst ) ; err != nil {
return err
2022-03-08 14:18:32 -05:00
}
2024-01-23 19:36:59 -05:00
} else {
return d . ArgErr ( )
2022-03-08 14:18:32 -05:00
}
}
return nil
}
func parseCaddyfile ( h httpcaddyfile . Helper ) ( caddyhttp . MiddlewareHandler , error ) {
var m Tracing
err := m . UnmarshalCaddyfile ( h . Dispenser )
return & m , err
}
// Interface guards
var (
_ caddy . Provisioner = ( * Tracing ) ( nil )
_ caddyhttp . MiddlewareHandler = ( * Tracing ) ( nil )
_ caddyfile . Unmarshaler = ( * Tracing ) ( nil )
)