Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 01:00 UTC
| 1 | package tracing |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "net/http" |
| 6 | |
| 7 | "go.uber.org/zap" |
| 8 | |
| 9 | "github.com/caddyserver/caddy/v2" |
| 10 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" |
| 11 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" |
| 12 | "github.com/caddyserver/caddy/v2/modules/caddyhttp" |
| 13 | ) |
| 14 | |
| 15 | func init() { |
| 16 | caddy.RegisterModule(Tracing{}) |
| 17 | httpcaddyfile.RegisterHandlerDirective("tracing", parseCaddyfile) |
| 18 | } |
| 19 | |
| 20 | // Tracing implements an HTTP handler that adds support for distributed tracing, |
| 21 | // using OpenTelemetry. This module is responsible for the injection and |
| 22 | // propagation of the trace context. Configure this module via environment |
| 23 | // variables (see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md). |
| 24 | // Some values can be overwritten in the configuration file. |
| 25 | type Tracing struct { |
| 26 | // SpanName is a span name. It should follow the naming guidelines here: |
| 27 | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span |
| 28 | SpanName string `json:"span"` |
| 29 | |
| 30 | // SpanAttributes are custom key-value pairs to be added to spans |
| 31 | SpanAttributes map[string]string `json:"span_attributes,omitempty"` |
| 32 | |
| 33 | // otel implements opentelemetry related logic. |
| 34 | otel openTelemetryWrapper |
| 35 | |
| 36 | logger *zap.Logger |
| 37 | } |
| 38 | |
| 39 | // CaddyModule returns the Caddy module information. |
| 40 | func (Tracing) CaddyModule() caddy.ModuleInfo { |
| 41 | return caddy.ModuleInfo{ |
| 42 | ID: "http.handlers.tracing", |
| 43 | New: func() caddy.Module { return new(Tracing) }, |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // Provision implements caddy.Provisioner. |
| 48 | func (ot *Tracing) Provision(ctx caddy.Context) error { |
| 49 | ot.logger = ctx.Logger() |
| 50 | |
| 51 | var err error |
| 52 | ot.otel, err = newOpenTelemetryWrapper(ctx, ot.SpanName, ot.SpanAttributes) |
| 53 | |
| 54 | return err |
| 55 | } |
| 56 | |
| 57 | // Cleanup implements caddy.CleanerUpper and closes any idle connections. It |
| 58 | // calls Shutdown method for a trace provider https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#shutdown. |
| 59 | func (ot *Tracing) Cleanup() error { |
| 60 | if err := ot.otel.cleanup(ot.logger); err != nil { |
| 61 | return fmt.Errorf("tracerProvider shutdown: %w", err) |
| 62 | } |
| 63 | return nil |
| 64 | } |
| 65 | |
| 66 | // ServeHTTP implements caddyhttp.MiddlewareHandler. |
| 67 | func (ot *Tracing) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { |
| 68 | return ot.otel.ServeHTTP(w, r, next) |
| 69 | } |
| 70 | |
| 71 | // UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax: |
| 72 | // |
| 73 | // tracing { |
| 74 | // [span <span_name>] |
| 75 | // [span_attributes { |
| 76 | // attr1 value1 |
| 77 | // attr2 value2 |
| 78 | // }] |
| 79 | // } |
| 80 | func (ot *Tracing) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { |
| 81 | setParameter := func(d *caddyfile.Dispenser, val *string) error { |
| 82 | if d.NextArg() { |
| 83 | *val = d.Val() |
| 84 | } else { |
| 85 | return d.ArgErr() |
| 86 | } |
| 87 | if d.NextArg() { |
| 88 | return d.ArgErr() |
| 89 | } |
| 90 | return nil |
| 91 | } |
| 92 | |
| 93 | // paramsMap is a mapping between "string" parameter from the Caddyfile and its destination within the module |
| 94 | paramsMap := map[string]*string{ |
| 95 | "span": &ot.SpanName, |
| 96 | } |
| 97 | |
| 98 | d.Next() // consume directive name |
| 99 | if d.NextArg() { |
| 100 | return d.ArgErr() |
| 101 | } |
| 102 | |
| 103 | for d.NextBlock(0) { |
| 104 | switch d.Val() { |
| 105 | case "span_attributes": |
| 106 | if ot.SpanAttributes == nil { |
| 107 | ot.SpanAttributes = make(map[string]string) |
| 108 | } |
| 109 | for d.NextBlock(1) { |
| 110 | key := d.Val() |
| 111 | if !d.NextArg() { |
| 112 | return d.ArgErr() |
| 113 | } |
| 114 | value := d.Val() |
| 115 | if d.NextArg() { |
| 116 | return d.ArgErr() |
| 117 | } |
| 118 | ot.SpanAttributes[key] = value |
| 119 | } |
| 120 | default: |
| 121 | if dst, ok := paramsMap[d.Val()]; ok { |
| 122 | if err := setParameter(d, dst); err != nil { |
| 123 | return err |
| 124 | } |
| 125 | } else { |
| 126 | return d.ArgErr() |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | return nil |
| 131 | } |
| 132 | |
| 133 | func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { |
| 134 | var m Tracing |
| 135 | err := m.UnmarshalCaddyfile(h.Dispenser) |
| 136 | return &m, err |
| 137 | } |
| 138 | |
| 139 | // Interface guards |
| 140 | var ( |
| 141 | _ caddy.Provisioner = (*Tracing)(nil) |
| 142 | _ caddyhttp.MiddlewareHandler = (*Tracing)(nil) |
| 143 | _ caddyfile.Unmarshaler = (*Tracing)(nil) |
| 144 | ) |