fix: otel not working without USER
This commit is contained in:
parent
ad566ee9ef
commit
c5ed376d5f
127 changed files with 347 additions and 305 deletions
|
|
@ -1,275 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
|
||||
otelsdk "go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
)
|
||||
|
||||
const (
|
||||
// AccessLogKeep is the keep string value.
|
||||
AccessLogKeep = "keep"
|
||||
// AccessLogDrop is the drop string value.
|
||||
AccessLogDrop = "drop"
|
||||
// AccessLogRedact is the redact string value.
|
||||
AccessLogRedact = "redact"
|
||||
)
|
||||
|
||||
const (
|
||||
// CommonFormat is the common logging format (CLF).
|
||||
CommonFormat string = "common"
|
||||
)
|
||||
|
||||
const OTelTraefikServiceName = "traefik"
|
||||
|
||||
// TraefikLog holds the configuration settings for the traefik logger.
|
||||
type TraefikLog struct {
|
||||
Level string `description:"Log level set to traefik logs." json:"level,omitempty" toml:"level,omitempty" yaml:"level,omitempty" export:"true"`
|
||||
Format string `description:"Traefik log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
|
||||
NoColor bool `description:"When using the 'common' format, disables the colorized output." json:"noColor,omitempty" toml:"noColor,omitempty" yaml:"noColor,omitempty" export:"true"`
|
||||
|
||||
FilePath string `description:"Traefik log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
|
||||
MaxSize int `description:"Maximum size in megabytes of the log file before it gets rotated." json:"maxSize,omitempty" toml:"maxSize,omitempty" yaml:"maxSize,omitempty" export:"true"`
|
||||
MaxAge int `description:"Maximum number of days to retain old log files based on the timestamp encoded in their filename." json:"maxAge,omitempty" toml:"maxAge,omitempty" yaml:"maxAge,omitempty" export:"true"`
|
||||
MaxBackups int `description:"Maximum number of old log files to retain." json:"maxBackups,omitempty" toml:"maxBackups,omitempty" yaml:"maxBackups,omitempty" export:"true"`
|
||||
Compress bool `description:"Determines if the rotated log files should be compressed using gzip." json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" export:"true"`
|
||||
|
||||
OTLP *OTelLog `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (l *TraefikLog) SetDefaults() {
|
||||
l.Format = CommonFormat
|
||||
l.Level = "ERROR"
|
||||
}
|
||||
|
||||
// AccessLog holds the configuration settings for the access logger (middlewares/accesslog).
|
||||
type AccessLog struct {
|
||||
FilePath string `description:"Access log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
|
||||
Format string `description:"Access log format: json, common, or genericCLF" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
|
||||
Filters *AccessLogFilters `description:"Access log filters, used to keep only specific access logs." json:"filters,omitempty" toml:"filters,omitempty" yaml:"filters,omitempty" export:"true"`
|
||||
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`
|
||||
BufferingSize int64 `description:"Number of access log lines to process in a buffered way." json:"bufferingSize,omitempty" toml:"bufferingSize,omitempty" yaml:"bufferingSize,omitempty" export:"true"`
|
||||
AddInternals bool `description:"Enables access log for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
|
||||
OTLP *OTelLog `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (l *AccessLog) SetDefaults() {
|
||||
l.Format = CommonFormat
|
||||
l.FilePath = ""
|
||||
l.Filters = &AccessLogFilters{}
|
||||
l.Fields = &AccessLogFields{}
|
||||
l.Fields.SetDefaults()
|
||||
}
|
||||
|
||||
// AccessLogFilters holds filters configuration.
|
||||
type AccessLogFilters struct {
|
||||
StatusCodes []string `description:"Keep access logs with status codes in the specified range." json:"statusCodes,omitempty" toml:"statusCodes,omitempty" yaml:"statusCodes,omitempty" export:"true"`
|
||||
RetryAttempts bool `description:"Keep access logs when at least one retry happened." json:"retryAttempts,omitempty" toml:"retryAttempts,omitempty" yaml:"retryAttempts,omitempty" export:"true"`
|
||||
MinDuration types.Duration `description:"Keep access logs when request took longer than the specified duration." json:"minDuration,omitempty" toml:"minDuration,omitempty" yaml:"minDuration,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// FieldHeaders holds configuration for access log headers.
|
||||
type FieldHeaders struct {
|
||||
DefaultMode string `description:"Default mode for fields: keep | drop | redact" json:"defaultMode,omitempty" toml:"defaultMode,omitempty" yaml:"defaultMode,omitempty" export:"true"`
|
||||
Names map[string]string `description:"Override mode for headers" json:"names,omitempty" toml:"names,omitempty" yaml:"names,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// AccessLogFields holds configuration for access log fields.
|
||||
type AccessLogFields struct {
|
||||
DefaultMode string `description:"Default mode for fields: keep | drop" json:"defaultMode,omitempty" toml:"defaultMode,omitempty" yaml:"defaultMode,omitempty" export:"true"`
|
||||
Names map[string]string `description:"Override mode for fields" json:"names,omitempty" toml:"names,omitempty" yaml:"names,omitempty" export:"true"`
|
||||
Headers *FieldHeaders `description:"Headers to keep, drop or redact" json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (f *AccessLogFields) SetDefaults() {
|
||||
f.DefaultMode = AccessLogKeep
|
||||
f.Headers = &FieldHeaders{
|
||||
DefaultMode: AccessLogDrop,
|
||||
}
|
||||
}
|
||||
|
||||
// Keep check if the field need to be kept or dropped.
|
||||
func (f *AccessLogFields) Keep(field string) bool {
|
||||
defaultKeep := true
|
||||
if f != nil {
|
||||
defaultKeep = checkFieldValue(f.DefaultMode, defaultKeep)
|
||||
|
||||
if v, ok := f.Names[field]; ok {
|
||||
return checkFieldValue(v, defaultKeep)
|
||||
}
|
||||
}
|
||||
return defaultKeep
|
||||
}
|
||||
|
||||
// KeepHeader checks if the headers need to be kept, dropped or redacted and returns the status.
|
||||
func (f *AccessLogFields) KeepHeader(header string) string {
|
||||
defaultValue := AccessLogKeep
|
||||
if f != nil && f.Headers != nil {
|
||||
defaultValue = checkFieldHeaderValue(f.Headers.DefaultMode, defaultValue)
|
||||
|
||||
if v, ok := f.Headers.Names[header]; ok {
|
||||
return checkFieldHeaderValue(v, defaultValue)
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func checkFieldValue(value string, defaultKeep bool) bool {
|
||||
switch value {
|
||||
case AccessLogKeep:
|
||||
return true
|
||||
case AccessLogDrop:
|
||||
return false
|
||||
default:
|
||||
return defaultKeep
|
||||
}
|
||||
}
|
||||
|
||||
func checkFieldHeaderValue(value, defaultValue string) string {
|
||||
if value == AccessLogKeep || value == AccessLogDrop || value == AccessLogRedact {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// OTelLog provides configuration settings for the open-telemetry logger.
|
||||
type OTelLog struct {
|
||||
ServiceName string `description:"Defines the service name resource attribute." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty"`
|
||||
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (o *OTelLog) SetDefaults() {
|
||||
o.ServiceName = OTelTraefikServiceName
|
||||
o.HTTP = &OTelHTTP{}
|
||||
o.HTTP.SetDefaults()
|
||||
}
|
||||
|
||||
// NewLoggerProvider creates a new OpenTelemetry logger provider.
|
||||
func (o *OTelLog) NewLoggerProvider(ctx context.Context) (*otelsdk.LoggerProvider, error) {
|
||||
var (
|
||||
err error
|
||||
exporter otelsdk.Exporter
|
||||
)
|
||||
if o.GRPC != nil {
|
||||
exporter, err = o.buildGRPCExporter()
|
||||
} else {
|
||||
exporter, err = o.buildHTTPExporter()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up exporter: %w", err)
|
||||
}
|
||||
|
||||
var resAttrs []attribute.KeyValue
|
||||
for k, v := range o.ResourceAttributes {
|
||||
resAttrs = append(resAttrs, attribute.String(k, v))
|
||||
}
|
||||
|
||||
res, err := resource.New(ctx,
|
||||
resource.WithContainer(),
|
||||
resource.WithHost(),
|
||||
resource.WithOS(),
|
||||
resource.WithProcess(),
|
||||
resource.WithTelemetrySDK(),
|
||||
resource.WithDetectors(K8sAttributesDetector{}),
|
||||
// The following order allows the user to override the service name and version,
|
||||
// as well as any other attributes set by the above detectors.
|
||||
resource.WithAttributes(
|
||||
semconv.ServiceName(o.ServiceName),
|
||||
semconv.ServiceVersion(version.Version),
|
||||
),
|
||||
resource.WithAttributes(resAttrs...),
|
||||
// Use the environment variables to allow overriding above resource attributes.
|
||||
resource.WithFromEnv(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building resource: %w", err)
|
||||
}
|
||||
|
||||
// Register the trace provider to allow the global logger to access it.
|
||||
bp := otelsdk.NewBatchProcessor(exporter)
|
||||
loggerProvider := otelsdk.NewLoggerProvider(
|
||||
otelsdk.WithResource(res),
|
||||
otelsdk.WithProcessor(bp),
|
||||
)
|
||||
|
||||
return loggerProvider, nil
|
||||
}
|
||||
|
||||
func (o *OTelLog) buildHTTPExporter() (*otlploghttp.Exporter, error) {
|
||||
endpoint, err := url.Parse(o.HTTP.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector endpoint %q: %w", o.HTTP.Endpoint, err)
|
||||
}
|
||||
|
||||
opts := []otlploghttp.Option{
|
||||
otlploghttp.WithEndpoint(endpoint.Host),
|
||||
otlploghttp.WithHeaders(o.HTTP.Headers),
|
||||
otlploghttp.WithCompression(otlploghttp.GzipCompression),
|
||||
}
|
||||
|
||||
if endpoint.Scheme == "http" {
|
||||
opts = append(opts, otlploghttp.WithInsecure())
|
||||
}
|
||||
|
||||
if endpoint.Path != "" {
|
||||
opts = append(opts, otlploghttp.WithURLPath(endpoint.Path))
|
||||
}
|
||||
|
||||
if o.HTTP.TLS != nil {
|
||||
tlsConfig, err := o.HTTP.TLS.CreateTLSConfig(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlploghttp.WithTLSClientConfig(tlsConfig))
|
||||
}
|
||||
|
||||
return otlploghttp.New(context.Background(), opts...)
|
||||
}
|
||||
|
||||
func (o *OTelLog) buildGRPCExporter() (*otlploggrpc.Exporter, error) {
|
||||
host, port, err := net.SplitHostPort(o.GRPC.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector endpoint %q: %w", o.GRPC.Endpoint, err)
|
||||
}
|
||||
|
||||
opts := []otlploggrpc.Option{
|
||||
otlploggrpc.WithEndpoint(fmt.Sprintf("%s:%s", host, port)),
|
||||
otlploggrpc.WithHeaders(o.GRPC.Headers),
|
||||
otlploggrpc.WithCompressor(gzip.Name),
|
||||
}
|
||||
|
||||
if o.GRPC.Insecure {
|
||||
opts = append(opts, otlploggrpc.WithInsecure())
|
||||
}
|
||||
|
||||
if o.GRPC.TLS != nil {
|
||||
tlsConfig, err := o.GRPC.TLS.CreateTLSConfig(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlploggrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
|
||||
}
|
||||
|
||||
return otlploggrpc.New(context.Background(), opts...)
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/traefik/paerser/types"
|
||||
)
|
||||
|
||||
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
|
||||
type Metrics struct {
|
||||
AddInternals bool `description:"Enables metrics for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
|
||||
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
InfluxDB2 *InfluxDB2 `description:"InfluxDB v2 metrics exporter type." json:"influxDB2,omitempty" toml:"influxDB2,omitempty" yaml:"influxDB2,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
OTLP *OTLP `description:"OpenTelemetry metrics exporter type." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// Prometheus can contain specific configuration used by the Prometheus Metrics exporter.
|
||||
type Prometheus struct {
|
||||
Buckets []float64 `description:"Buckets for latency metrics." json:"buckets,omitempty" toml:"buckets,omitempty" yaml:"buckets,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
EntryPoint string `description:"EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"`
|
||||
ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty" export:"true"`
|
||||
HeaderLabels map[string]string `description:"Defines the extra labels for the requests_total metrics, and for each of them, the request header containing the value for this label." json:"headerLabels,omitempty" toml:"headerLabels,omitempty" yaml:"headerLabels,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (p *Prometheus) SetDefaults() {
|
||||
p.Buckets = []float64{0.1, 0.3, 1.2, 5}
|
||||
p.AddEntryPointsLabels = true
|
||||
p.AddServicesLabels = true
|
||||
p.EntryPoint = "traefik"
|
||||
}
|
||||
|
||||
// Datadog contains address and metrics pushing interval configuration.
|
||||
type Datadog struct {
|
||||
Address string `description:"Datadog's address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
PushInterval types.Duration `description:"Datadog push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
Prefix string `description:"Prefix to use for metrics collection." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (d *Datadog) SetDefaults() {
|
||||
host, ok := os.LookupEnv("DD_AGENT_HOST")
|
||||
if !ok {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
port, ok := os.LookupEnv("DD_DOGSTATSD_PORT")
|
||||
if !ok {
|
||||
port = "8125"
|
||||
}
|
||||
d.Address = net.JoinHostPort(host, port)
|
||||
d.PushInterval = types.Duration(10 * time.Second)
|
||||
d.AddEntryPointsLabels = true
|
||||
d.AddServicesLabels = true
|
||||
d.Prefix = "traefik"
|
||||
}
|
||||
|
||||
// Statsd contains address and metrics pushing interval configuration.
|
||||
type Statsd struct {
|
||||
Address string `description:"StatsD address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
PushInterval types.Duration `description:"StatsD push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
Prefix string `description:"Prefix to use for metrics collection." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (s *Statsd) SetDefaults() {
|
||||
s.Address = "localhost:8125"
|
||||
s.PushInterval = types.Duration(10 * time.Second)
|
||||
s.AddEntryPointsLabels = true
|
||||
s.AddServicesLabels = true
|
||||
s.Prefix = "traefik"
|
||||
}
|
||||
|
||||
// InfluxDB2 contains address, token and metrics pushing interval configuration.
|
||||
type InfluxDB2 struct {
|
||||
Address string `description:"InfluxDB v2 address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
Token string `description:"InfluxDB v2 access token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
|
||||
PushInterval types.Duration `description:"InfluxDB v2 push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
Org string `description:"InfluxDB v2 org ID." json:"org,omitempty" toml:"org,omitempty" yaml:"org,omitempty" export:"true"`
|
||||
Bucket string `description:"InfluxDB v2 bucket ID." json:"bucket,omitempty" toml:"bucket,omitempty" yaml:"bucket,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
AdditionalLabels map[string]string `description:"Additional labels (influxdb tags) on all metrics" json:"additionalLabels,omitempty" toml:"additionalLabels,omitempty" yaml:"additionalLabels,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (i *InfluxDB2) SetDefaults() {
|
||||
i.Address = "http://localhost:8086"
|
||||
i.PushInterval = types.Duration(10 * time.Second)
|
||||
i.AddEntryPointsLabels = true
|
||||
i.AddServicesLabels = true
|
||||
}
|
||||
|
||||
// OTLP contains specific configuration used by the OpenTelemetry Metrics exporter.
|
||||
type OTLP struct {
|
||||
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
ExplicitBoundaries []float64 `description:"Boundaries for latency metrics." json:"explicitBoundaries,omitempty" toml:"explicitBoundaries,omitempty" yaml:"explicitBoundaries,omitempty" export:"true"`
|
||||
PushInterval types.Duration `description:"Period between calls to collect a checkpoint." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
ServiceName string `description:"Defines the service name resource attribute." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (o *OTLP) SetDefaults() {
|
||||
o.HTTP = &OTelHTTP{}
|
||||
o.HTTP.SetDefaults()
|
||||
|
||||
o.AddEntryPointsLabels = true
|
||||
o.AddServicesLabels = true
|
||||
o.ExplicitBoundaries = []float64{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10}
|
||||
o.PushInterval = types.Duration(10 * time.Second)
|
||||
o.ServiceName = OTelTraefikServiceName
|
||||
}
|
||||
|
||||
// Statistics provides options for monitoring request and response stats.
|
||||
type Statistics struct {
|
||||
RecentErrors int `description:"Number of recent errors logged." json:"recentErrors,omitempty" toml:"recentErrors,omitempty" yaml:"recentErrors,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (s *Statistics) SetDefaults() {
|
||||
s.RecentErrors = 10
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package types
|
||||
|
||||
// OTelGRPC provides configuration settings for the gRPC open-telemetry.
|
||||
type OTelGRPC struct {
|
||||
Endpoint string `description:"Sets the gRPC endpoint (host:port) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
Insecure bool `description:"Disables client transport security for the exporter." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
|
||||
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (o *OTelGRPC) SetDefaults() {
|
||||
o.Endpoint = "localhost:4317"
|
||||
}
|
||||
|
||||
// OTelHTTP provides configuration settings for the HTTP open-telemetry.
|
||||
type OTelHTTP struct {
|
||||
Endpoint string `description:"Sets the HTTP endpoint (scheme://host:port/path) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (o *OTelHTTP) SetDefaults() {
|
||||
o.Endpoint = "https://localhost:4318"
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
)
|
||||
|
||||
type TracingVerbosity string
|
||||
|
||||
const (
|
||||
MinimalVerbosity TracingVerbosity = "minimal"
|
||||
DetailedVerbosity TracingVerbosity = "detailed"
|
||||
)
|
||||
|
||||
func (v TracingVerbosity) Allows(verbosity TracingVerbosity) bool {
|
||||
switch v {
|
||||
case DetailedVerbosity:
|
||||
return verbosity == DetailedVerbosity || verbosity == MinimalVerbosity
|
||||
default:
|
||||
return verbosity == MinimalVerbosity
|
||||
}
|
||||
}
|
||||
|
||||
// OTelTracing provides configuration settings for the open-telemetry tracer.
|
||||
type OTelTracing struct {
|
||||
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (c *OTelTracing) SetDefaults() {
|
||||
c.HTTP = &OTelHTTP{}
|
||||
c.HTTP.SetDefaults()
|
||||
}
|
||||
|
||||
// Setup sets up the tracer.
|
||||
func (c *OTelTracing) Setup(ctx context.Context, serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error) {
|
||||
var (
|
||||
err error
|
||||
exporter *otlptrace.Exporter
|
||||
)
|
||||
if c.GRPC != nil {
|
||||
exporter, err = c.setupGRPCExporter()
|
||||
} else {
|
||||
exporter, err = c.setupHTTPExporter()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("setting up exporter: %w", err)
|
||||
}
|
||||
|
||||
var resAttrs []attribute.KeyValue
|
||||
for k, v := range resourceAttributes {
|
||||
resAttrs = append(resAttrs, attribute.String(k, v))
|
||||
}
|
||||
|
||||
res, err := resource.New(ctx,
|
||||
resource.WithContainer(),
|
||||
resource.WithHost(),
|
||||
resource.WithOS(),
|
||||
resource.WithProcess(),
|
||||
resource.WithTelemetrySDK(),
|
||||
resource.WithDetectors(K8sAttributesDetector{}),
|
||||
// The following order allows the user to override the service name and version,
|
||||
// as well as any other attributes set by the above detectors.
|
||||
resource.WithAttributes(
|
||||
semconv.ServiceName(serviceName),
|
||||
semconv.ServiceVersion(version.Version),
|
||||
),
|
||||
resource.WithAttributes(resAttrs...),
|
||||
// Use the environment variables to allow overriding above resource attributes.
|
||||
resource.WithFromEnv(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("building resource: %w", err)
|
||||
}
|
||||
|
||||
// Register the trace exporter with a TracerProvider, using a batch
|
||||
// span processor to aggregate spans before export.
|
||||
bsp := sdktrace.NewBatchSpanProcessor(exporter)
|
||||
tracerProvider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(sampleRate)),
|
||||
sdktrace.WithResource(res),
|
||||
sdktrace.WithSpanProcessor(bsp),
|
||||
)
|
||||
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
|
||||
log.Debug().Msg("OpenTelemetry tracer configured")
|
||||
|
||||
return tracerProvider.Tracer("github.com/traefik/traefik"), &tpCloser{provider: tracerProvider}, err
|
||||
}
|
||||
|
||||
func (c *OTelTracing) setupHTTPExporter() (*otlptrace.Exporter, error) {
|
||||
endpoint, err := url.Parse(c.HTTP.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector endpoint %q: %w", c.HTTP.Endpoint, err)
|
||||
}
|
||||
|
||||
opts := []otlptracehttp.Option{
|
||||
otlptracehttp.WithEndpoint(endpoint.Host),
|
||||
otlptracehttp.WithHeaders(c.HTTP.Headers),
|
||||
otlptracehttp.WithCompression(otlptracehttp.GzipCompression),
|
||||
}
|
||||
|
||||
if endpoint.Scheme == "http" {
|
||||
opts = append(opts, otlptracehttp.WithInsecure())
|
||||
}
|
||||
|
||||
if endpoint.Path != "" {
|
||||
opts = append(opts, otlptracehttp.WithURLPath(endpoint.Path))
|
||||
}
|
||||
|
||||
if c.HTTP.TLS != nil {
|
||||
tlsConfig, err := c.HTTP.TLS.CreateTLSConfig(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlptracehttp.WithTLSClientConfig(tlsConfig))
|
||||
}
|
||||
|
||||
return otlptrace.New(context.Background(), otlptracehttp.NewClient(opts...))
|
||||
}
|
||||
|
||||
func (c *OTelTracing) setupGRPCExporter() (*otlptrace.Exporter, error) {
|
||||
host, port, err := net.SplitHostPort(c.GRPC.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collector endpoint %q: %w", c.GRPC.Endpoint, err)
|
||||
}
|
||||
|
||||
opts := []otlptracegrpc.Option{
|
||||
otlptracegrpc.WithEndpoint(fmt.Sprintf("%s:%s", host, port)),
|
||||
otlptracegrpc.WithHeaders(c.GRPC.Headers),
|
||||
otlptracegrpc.WithCompressor(gzip.Name),
|
||||
}
|
||||
|
||||
if c.GRPC.Insecure {
|
||||
opts = append(opts, otlptracegrpc.WithInsecure())
|
||||
}
|
||||
|
||||
if c.GRPC.TLS != nil {
|
||||
tlsConfig, err := c.GRPC.TLS.CreateTLSConfig(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating TLS client config: %w", err)
|
||||
}
|
||||
|
||||
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
|
||||
}
|
||||
|
||||
return otlptrace.New(context.Background(), otlptracegrpc.NewClient(opts...))
|
||||
}
|
||||
|
||||
// tpCloser converts a TraceProvider into an io.Closer.
|
||||
type tpCloser struct {
|
||||
provider *sdktrace.TracerProvider
|
||||
}
|
||||
|
||||
func (t *tpCloser) Close() error {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
|
||||
defer cancel()
|
||||
|
||||
return t.provider.Shutdown(ctx)
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTracingVerbosity_Allows(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
from TracingVerbosity
|
||||
to TracingVerbosity
|
||||
allows bool
|
||||
}{
|
||||
{
|
||||
desc: "minimal vs minimal",
|
||||
from: MinimalVerbosity,
|
||||
to: MinimalVerbosity,
|
||||
allows: true,
|
||||
},
|
||||
{
|
||||
desc: "minimal vs detailed",
|
||||
from: MinimalVerbosity,
|
||||
to: DetailedVerbosity,
|
||||
allows: false,
|
||||
},
|
||||
{
|
||||
desc: "detailed vs minimal",
|
||||
from: DetailedVerbosity,
|
||||
to: MinimalVerbosity,
|
||||
allows: true,
|
||||
},
|
||||
{
|
||||
desc: "detailed vs detailed",
|
||||
from: DetailedVerbosity,
|
||||
to: DetailedVerbosity,
|
||||
allows: true,
|
||||
},
|
||||
{
|
||||
desc: "unknown vs minimal",
|
||||
from: TracingVerbosity("unknown"),
|
||||
to: MinimalVerbosity,
|
||||
allows: true,
|
||||
},
|
||||
{
|
||||
desc: "unknown vs detailed",
|
||||
from: TracingVerbosity("unknown"),
|
||||
to: DetailedVerbosity,
|
||||
allows: false,
|
||||
},
|
||||
{
|
||||
desc: "minimal vs unknown",
|
||||
from: MinimalVerbosity,
|
||||
to: TracingVerbosity("unknown"),
|
||||
allows: false,
|
||||
},
|
||||
{
|
||||
desc: "detailed vs unknown",
|
||||
from: DetailedVerbosity,
|
||||
to: TracingVerbosity("unknown"),
|
||||
allows: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, test.allows, test.from.Allows(test.to))
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue