diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0218eb..f429b7bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,51 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [3.0.1] - March 14, 2023 +## [3.2.0] - December 13, 2023 + +### New Features + +- Added support for including `traceId` and `spanId` into the logs. ([#407](https://github.com/optimizely/agent/pull/407)) + +## [3.1.0] - November 3, 2023 + +### New Features + +- Added support for Prometheus-based metrics alongside expvar metrics. This can be configured from the config.yaml file. ([#348](https://github.com/optimizely/agent/pull/348)) + +- Added support for OpenTelemetry tracing. Distributed tracing is also supported according to [W3C TraceContext](https://www.w3.org/TR/trace-context/). ([#400](https://github.com/optimizely/agent/pull/400), [#401](https://github.com/optimizely/agent/pull/401), [#402](https://github.com/optimizely/agent/pull/402)) + +## [4.0.0-beta] - May 11, 2023 + +### New Features + +The 4.0.0-beta release introduces a new primary feature, [Advanced Audience Targeting](https://docs.developers.optimizely.com/feature-experimentation/docs/optimizely-data-platform-advanced-audience-targeting) enabled through integration with [Optimizely Data Platform (ODP)](https://docs.developers.optimizely.com/optimizely-data-platform/docs) ([#356](https://github.com/optimizely/agent/pull/356), [#364](https://github.com/optimizely/agent/pull/364), [#365](https://github.com/optimizely/agent/pull/365), [#366](https://github.com/optimizely/agent/pull/366)). + +You can use ODP, a high-performance [Customer Data Platform (CDP)](https://www.optimizely.com/optimization-glossary/customer-data-platform/), to easily create complex real-time segments (RTS) using first-party and 50+ third-party data sources out of the box. You can create custom schemas that support the user attributes important for your business, and stitch together user behavior done on different devices to better understand and target your customers for personalized user experiences. ODP can be used as a single source of truth for these segments in any Optimizely or 3rd party tool. + +With ODP accounts integrated into Optimizely projects, you can build audiences using segments pre-defined in ODP. The SDK will fetch the segments for given users and make decisions using the segments. For access to ODP audience targeting in your Feature Experimentation account, please contact your Optimizely Customer Success Manager. + +This version includes the following changes: + +- `FetchQualifiedSegments()` API has been added to the `/decide` endpoint. This API will retrieve user segments from the ODP server. The fetched segments will be used for audience evaluation. Fetched data will be stored in the local cache to avoid repeated network delays. + +- `SendOdpEvent()` API has been added with the `/send-opd-event` endpoint. Customers can build/send arbitrary ODP events that will bind user identifiers and data to user profiles in ODP. + +For details, refer to our documentation pages: + +* [Advanced Audience Targeting](https://docs.developers.optimizely.com/feature-experimentation/docs/optimizely-data-platform-advanced-audience-targeting) + +* [Server SDK Support](https://docs.developers.optimizely.com/feature-experimentation/docs/advanced-audience-targeting-for-server-side-sdks) + +* [Use Optimizely Agent](https://docs.developers.optimizely.com/feature-experimentation/docs/use-optimizely-agent) + +* [Configure Optimizely Agent](https://docs.developers.optimizely.com/feature-experimentation/docs/configure-optimizely-agent) + +### Breaking Changes + +- ODPManager in the SDK is enabled by default. Unless an ODP account is integrated into the Optimizely projects, most ODPManager functions will be ignored. If needed, ODPManager can be disabled when OptimizelyClient is instantiated. From Agent, it can be switched off from config.yaml or env variables. + +## [3.0.1] - March 16, 2023 - Update README.md and other non-functional code to reflect that this SDK supports both Optimizely Feature Experimentation and Optimizely Full Stack. ([#369](https://github.com/optimizely/agent/pull/369)). diff --git a/cmd/optimizely/main.go b/cmd/optimizely/main.go index 7d5958e5..1e64396d 100644 --- a/cmd/optimizely/main.go +++ b/cmd/optimizely/main.go @@ -18,6 +18,8 @@ package main import ( "bytes" "context" + "errors" + "fmt" "os" "os/signal" "runtime" @@ -42,6 +44,15 @@ import ( // Initiate the loading of the userprofileservice plugins _ "github.com/optimizely/agent/plugins/userprofileservice/all" "github.com/optimizely/go-sdk/pkg/logging" + "go.opentelemetry.io/otel" + "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/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" ) // Version holds the admin version @@ -112,6 +123,95 @@ func initLogging(conf config.LogConfig) { } } +func getStdOutTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + f, err := os.Create(conf.Services.StdOut.Filename) + if err != nil { + return nil, fmt.Errorf("failed to create the trace file, error: %s", err.Error()) + } + + exp, err := stdouttrace.New( + stdouttrace.WithPrettyPrint(), + stdouttrace.WithWriter(f), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the collector exporter, error: %s", err.Error()) + } + + res, err := resource.New( + context.Background(), + resource.WithAttributes( + semconv.ServiceNameKey.String(conf.ServiceName), + semconv.DeploymentEnvironmentKey.String(conf.Env), + ), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the otel resource, error: %s", err.Error()) + } + + return sdktrace.NewTracerProvider( + sdktrace.WithBatcher(exp), + sdktrace.WithResource(res), + ), nil +} + +func getOTELTraceClient(conf config.OTELTracingConfig) (otlptrace.Client, error) { + switch conf.Services.Remote.Protocol { + case config.TracingRemoteProtocolHTTP: + return otlptracehttp.NewClient( + otlptracehttp.WithInsecure(), + otlptracehttp.WithEndpoint(conf.Services.Remote.Endpoint), + ), nil + case config.TracingRemoteProtocolGRPC: + return otlptracegrpc.NewClient( + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithEndpoint(conf.Services.Remote.Endpoint), + ), nil + default: + return nil, errors.New("unknown remote tracing protocal") + } +} + +func getRemoteTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + res, err := resource.New( + context.Background(), + resource.WithAttributes( + semconv.ServiceNameKey.String(conf.ServiceName), + semconv.DeploymentEnvironmentKey.String(conf.Env), + ), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the otel resource, error: %s", err.Error()) + } + + traceClient, err := getOTELTraceClient(conf) + if err != nil { + return nil, fmt.Errorf("failed to create the remote trace client, error: %s", err.Error()) + } + + traceExporter, err := otlptrace.New(context.Background(), traceClient) + if err != nil { + return nil, fmt.Errorf("failed to create the remote trace exporter, error: %s", err.Error()) + } + + bsp := sdktrace.NewBatchSpanProcessor(traceExporter) + return sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(conf.Services.Remote.SampleRate))), + sdktrace.WithResource(res), + sdktrace.WithSpanProcessor(bsp), + ), nil +} + +func initTracing(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + switch conf.Default { + case config.TracingServiceTypeRemote: + return getRemoteTraceProvider(conf) + case config.TracingServiceTypeStdOut: + return getStdOutTraceProvider(conf) + default: + return nil, errors.New("unknown tracing service type") + } +} + func setRuntimeEnvironment(conf config.RuntimeConfig) { if conf.BlockProfileRate != 0 { log.Warn().Msgf("Setting non-zero blockProfileRate is NOT recommended for production") @@ -133,11 +233,29 @@ func main() { conf := loadConfig(v) initLogging(conf.Log) + if conf.Tracing.Enabled { + tp, err := initTracing(conf.Tracing.OpenTelemetry) + if err != nil { + log.Panic().Err(err).Msg("Unable to initialize tracing") + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Error().Err(err).Msg("Failed to shutdown tracing") + } + }() + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.TraceContext{}) + log.Info().Msg(fmt.Sprintf("Tracing enabled with service %q", conf.Tracing.OpenTelemetry.Default)) + } else { + log.Info().Msg("Tracing disabled") + } + conf.LogConfigWarnings() setRuntimeEnvironment(conf.Runtime) - agentMetricsRegistry := metrics.NewRegistry() + // Set metrics type to be used + agentMetricsRegistry := metrics.NewRegistry(conf.Admin.MetricsType) sdkMetricsRegistry := optimizely.NewRegistry(agentMetricsRegistry) ctx, cancel := context.WithCancel(context.Background()) // Create default service context diff --git a/cmd/optimizely/main_test.go b/cmd/optimizely/main_test.go index 6d7f9828..d23e989a 100644 --- a/cmd/optimizely/main_test.go +++ b/cmd/optimizely/main_test.go @@ -98,6 +98,7 @@ func assertLog(t *testing.T, actual config.LogConfig) { func assertAdmin(t *testing.T, actual config.AdminConfig) { assert.Equal(t, "3002", actual.Port) + assert.Equal(t, "prometheus", actual.MetricsType) } func assertAdminAuth(t *testing.T, actual config.ServiceAuthConfig) { @@ -232,6 +233,7 @@ func TestViperProps(t *testing.T) { v.Set("log.level", "debug") v.Set("admin.port", "3002") + v.Set("admin.metricsType", "prometheus") v.Set("admin.auth.ttl", "30m") v.Set("admin.auth.hmacSecrets", "efgh,ijkl") v.Set("admin.auth.jwksURL", "admin_jwks_url") @@ -319,6 +321,7 @@ func TestViperEnv(t *testing.T) { _ = os.Setenv("OPTIMIZELY_LOG_LEVEL", "debug") _ = os.Setenv("OPTIMIZELY_ADMIN_PORT", "3002") + _ = os.Setenv("OPTIMIZELY_ADMIN_METRICSTYPE", "prometheus") _ = os.Setenv("OPTIMIZELY_API_MAXCONNS", "100") _ = os.Setenv("OPTIMIZELY_API_PORT", "3000") @@ -359,3 +362,92 @@ func TestLoggingWithIncludeSdkKey(t *testing.T) { }) assert.False(t, optimizely.ShouldIncludeSDKKey) } + +func Test_initTracing(t *testing.T) { + type args struct { + conf config.OTELTracingConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "should return error when exporter type is not supported", + args: args{ + conf: config.OTELTracingConfig{ + Default: "unsupported", + }, + }, + wantErr: true, + }, + { + name: "should return no error stdout tracing exporter", + args: args{ + conf: config.OTELTracingConfig{ + Default: "stdout", + Services: config.TracingServiceConfig{ + StdOut: config.TracingStdOutConfig{ + Filename: "trace.out", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with http protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "http", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with grpc protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "grpc", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with invalid protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "udp/invalid", + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := initTracing(tt.args.conf) + if (err != nil) != tt.wantErr { + t.Errorf("initTracing() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/cmd/optimizely/testdata/default.yaml b/cmd/optimizely/testdata/default.yaml index 8abb6ad6..972189de 100644 --- a/cmd/optimizely/testdata/default.yaml +++ b/cmd/optimizely/testdata/default.yaml @@ -52,6 +52,7 @@ client: admin: port: "3002" + metricsType: "prometheus" auth: ttl: 30m hmacSecrets: diff --git a/config.yaml b/config.yaml index 6943496f..b8b85779 100644 --- a/config.yaml +++ b/config.yaml @@ -26,6 +26,44 @@ log: ## to set whether or not the SDK key is included in the logging output. includeSdkKey: true +## +## tracing: tracing configuration +## +## For distributed tracing, trace context should be sent on "traceparent" header +## The value set in HTTP Header must be a hex compliant with the W3C trace-context specification. +## See more at https://www.w3.org/TR/trace-context/#trace-id +tracing: + ## bydefault tracing is disabled + ## to enable tracing set enabled to true + enabled: false + # opentelemetry tracing configuration + opentelemetry: + ## bydefault stdout exporter is enabled + ## to enable remote exporter set default as "remote" + default: "stdout" + ## tracing service name + serviceName: "optimizely-agent" + ## tracing environment name + ## example: for production environment env can be set as "prod" + env: "dev" + ## tracing service configuration + services: + ## stdout exporter configuration + stdout: + ## for stdout tracing data is saved in the specified file + filename: "trace.out" + ## remote exporter configuration + remote: + ## remote collector endpoint + endpoint: "localhost:4317" + ## supported protocols are "http" and "grpc" + protocol: "grpc" + ## "sampleRate" refers to the rate at which traces are collected and recorded. + ## sampleRate >= 1 will always sample. + ## sampleRate < 0 are treated as zero i.e. never sample. + sampleRate: 1.0 + + ## ## http server configuration ## @@ -95,6 +133,10 @@ api: admin: ## http listener port port: "8088" + ## metrics package to use + ## supported packages are expvar and prometheus + ## default is expvar + metricsType: "" ## ## webhook service receives update notifications to your Optimizely project. Receipt of the webhook will ## trigger an immediate download of the datafile from the CDN diff --git a/config/config.go b/config/config.go index ae2a49df..4155b3ef 100644 --- a/config/config.go +++ b/config/config.go @@ -39,7 +39,8 @@ func NewDefaultConfig() *AgentConfig { JwksURL: "", JwksUpdateInterval: 0, }, - Port: "8088", + Port: "8088", + MetricsType: "expvar", }, API: APIConfig{ Auth: ServiceAuthConfig{ @@ -70,6 +71,9 @@ func NewDefaultConfig() *AgentConfig { IncludeSDKKey: true, Level: "info", }, + Tracing: TracingConfig{ + Enabled: false, + }, Client: ClientConfig{ PollingInterval: 1 * time.Minute, BatchSize: 10, @@ -123,6 +127,7 @@ type AgentConfig struct { Admin AdminConfig `json:"admin"` API APIConfig `json:"api"` Log LogConfig `json:"log"` + Tracing TracingConfig `json:"tracing"` Client ClientConfig `json:"client"` Runtime RuntimeConfig `json:"runtime"` Server ServerConfig `json:"server"` @@ -172,6 +177,47 @@ type LogConfig struct { Level string `json:"level"` } +type TracingConfig struct { + Enabled bool `json:"enabled"` + OpenTelemetry OTELTracingConfig `json:"opentelemetry"` +} + +type TracingServiceType string + +const ( + TracingServiceTypeStdOut TracingServiceType = "stdout" + TracingServiceTypeRemote TracingServiceType = "remote" +) + +type TracingRemoteProtocol string + +const ( + TracingRemoteProtocolGRPC TracingRemoteProtocol = "grpc" + TracingRemoteProtocolHTTP TracingRemoteProtocol = "http" +) + +type OTELTracingConfig struct { + Default TracingServiceType `json:"default"` + ServiceName string `json:"serviceName"` + Env string `json:"env"` + Services TracingServiceConfig `json:"services"` +} + +type TracingServiceConfig struct { + StdOut TracingStdOutConfig `json:"stdout"` + Remote TracingRemoteConfig `json:"remote"` +} + +type TracingStdOutConfig struct { + Filename string `json:"filename"` +} + +type TracingRemoteConfig struct { + Endpoint string `json:"endpoint"` + Protocol TracingRemoteProtocol `json:"protocol"` + SampleRate float64 `json:"sampleRate"` +} + // PluginConfigs defines the generic mapping of middleware plugins type PluginConfigs map[string]interface{} @@ -227,8 +273,9 @@ type CORSConfig struct { // AdminConfig holds the configuration for the admin web interface type AdminConfig struct { - Auth ServiceAuthConfig `json:"-"` - Port string `json:"port"` + Auth ServiceAuthConfig `json:"-"` + Port string `json:"port"` + MetricsType string `json:"metricsType"` } // WebhookConfig holds configuration for Optimizely Webhooks diff --git a/config/config_test.go b/config/config_test.go index 4cc52bd2..464991d5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -50,6 +50,7 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, "info", conf.Log.Level) assert.Equal(t, "8088", conf.Admin.Port) + assert.Equal(t, "expvar", conf.Admin.MetricsType) assert.Equal(t, make([]OAuthClientCredentials, 0), conf.Admin.Auth.Clients) assert.Equal(t, make([]string, 0), conf.Admin.Auth.HMACSecrets) assert.Equal(t, time.Duration(0), conf.Admin.Auth.TTL) diff --git a/go.mod b/go.mod index 57fff9f0..1d199067 100644 --- a/go.mod +++ b/go.mod @@ -14,15 +14,43 @@ require ( github.com/lestrrat-go/jwx v0.9.0 github.com/optimizely/go-sdk v1.8.4-0.20230308225321-5a0d450ca834 github.com/orcaman/concurrent-map v1.0.0 + github.com/prometheus/client_golang v1.11.0 github.com/rakyll/statik v0.1.7 github.com/rs/zerolog v1.29.0 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 - golang.org/x/crypto v0.6.0 - golang.org/x/sync v0.1.0 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 + go.opentelemetry.io/otel/sdk v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 + golang.org/x/crypto v0.11.0 + golang.org/x/sync v0.3.0 gopkg.in/yaml.v2 v2.4.0 ) +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.30.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + golang.org/x/net v0.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/grpc v1.58.2 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) + require ( github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -51,8 +79,8 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/twmb/murmur3 v1.1.6 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.11.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4bc5c6a4..b1e14991 100644 --- a/go.sum +++ b/go.sum @@ -42,7 +42,19 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -81,14 +93,28 @@ github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIo github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -113,6 +139,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -124,6 +153,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -147,6 +177,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -159,14 +191,23 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -181,13 +222,19 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= @@ -197,6 +244,7 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -204,16 +252,39 @@ github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs= github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -225,6 +296,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -236,8 +308,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/twmb/murmur3 v1.0.0/go.mod h1:5Y5m8Y8WIyucaICVP+Aep5C8ydggjEuRQHDq1icoOYo= @@ -253,6 +325,26 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -260,8 +352,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -297,6 +389,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -304,6 +397,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -326,7 +420,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -336,6 +432,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -346,12 +443,15 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -360,6 +460,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -372,6 +473,8 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -379,16 +482,19 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -396,8 +502,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -514,6 +621,11 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -530,6 +642,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -540,14 +654,24 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/handlers/activate.go b/pkg/handlers/activate.go index 0a35d662..2768f919 100644 --- a/pkg/handlers/activate.go +++ b/pkg/handlers/activate.go @@ -22,13 +22,12 @@ import ( "fmt" "net/http" + "github.com/go-chi/render" + "github.com/optimizely/agent/pkg/middleware" "github.com/optimizely/agent/pkg/optimizely" - "github.com/optimizely/go-sdk/pkg/config" "github.com/optimizely/go-sdk/pkg/entities" - - "github.com/go-chi/render" ) type keyMap map[string]string @@ -42,12 +41,13 @@ type ActivateBody struct { // Activate makes feature and experiment decisions for the selected query parameters. func Activate(w http.ResponseWriter, r *http.Request) { optlyClient, err := middleware.GetOptlyClient(r) - logger := middleware.GetLogger(r) if err != nil { RenderError(err, http.StatusInternalServerError, w, r) return } + logger := middleware.GetLogger(r) + uc, err := getUserContext(r) if err != nil { RenderError(err, http.StatusBadRequest, w, r) @@ -76,10 +76,10 @@ func Activate(w http.ResponseWriter, r *http.Request) { switch value { case "experiment": logger.Debug().Str("experimentKey", key).Msg("fetching experiment decision") - d, err = optlyClient.ActivateExperiment(key, uc, disableTracking) + d, err = optlyClient.ActivateExperiment(r.Context(), key, uc, disableTracking) case "feature": logger.Debug().Str("featureKey", key).Msg("fetching feature decision") - d, err = optlyClient.ActivateFeature(key, uc, disableTracking) + d, err = optlyClient.ActivateFeature(r.Context(), key, uc, disableTracking) case "experimentKey-not-found": logger.Debug().Str("experimentKey", key).Msg("experimentKey not found") d = &optimizely.Decision{ @@ -108,6 +108,7 @@ func Activate(w http.ResponseWriter, r *http.Request) { } decisions = filterDecisions(r, decisions) + logger.Info().Msgf("Made activate decisions for user %s", uc.ID) render.JSON(w, r, decisions) } diff --git a/pkg/handlers/admin_entities.go b/pkg/handlers/admin_entities.go index 9b555fd5..1c6e9abc 100644 --- a/pkg/handlers/admin_entities.go +++ b/pkg/handlers/admin_entities.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019-2020, Optimizely, Inc. and contributors * + * Copyright 2019-2020,2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -18,14 +18,13 @@ package handlers import ( - "expvar" "net/http" "os" "time" "github.com/go-chi/render" - "github.com/optimizely/agent/config" + "github.com/optimizely/agent/pkg/metrics" ) var startTime = time.Now() @@ -87,5 +86,5 @@ func (a Admin) AppInfoHeader(next http.Handler) http.Handler { // Metrics returns expvar info func (a Admin) Metrics(w http.ResponseWriter, r *http.Request) { - expvar.Handler().ServeHTTP(w, r) + metrics.GetHandler(a.Config.Admin.MetricsType).ServeHTTP(w, r) } diff --git a/pkg/handlers/decide.go b/pkg/handlers/decide.go index 15331b70..021645d2 100644 --- a/pkg/handlers/decide.go +++ b/pkg/handlers/decide.go @@ -21,14 +21,13 @@ import ( "errors" "net/http" - "github.com/optimizely/agent/pkg/middleware" + "github.com/go-chi/render" + "github.com/optimizely/agent/pkg/middleware" "github.com/optimizely/go-sdk/pkg/client" "github.com/optimizely/go-sdk/pkg/decide" "github.com/optimizely/go-sdk/pkg/decision" "github.com/optimizely/go-sdk/pkg/odp/segment" - - "github.com/go-chi/render" ) // DecideBody defines the request body for decide API @@ -108,6 +107,7 @@ func Decide(w http.ResponseWriter, r *http.Request) { key := keys[0] logger.Debug().Str("featureKey", key).Msg("fetching feature decision") d := optimizelyUserContext.Decide(key, decideOptions) + logger.Info().Msgf("Feature %q is enabled for user %s? %t", d.FlagKey, d.UserContext.UserID, d.Enabled) decideOut := DecideOut{d, d.Variables.ToMap()} render.JSON(w, r, decideOut) return @@ -120,6 +120,7 @@ func Decide(w http.ResponseWriter, r *http.Request) { for _, d := range decides { decideOut := DecideOut{d, d.Variables.ToMap()} decideOuts = append(decideOuts, decideOut) + logger.Info().Msgf("Feature %q is enabled for user %s? %t", d.FlagKey, d.UserContext.UserID, d.Enabled) } render.JSON(w, r, decideOuts) } diff --git a/pkg/handlers/decide_test.go b/pkg/handlers/decide_test.go index c047da4d..7eef55c2 100644 --- a/pkg/handlers/decide_test.go +++ b/pkg/handlers/decide_test.go @@ -26,18 +26,17 @@ import ( "net/http/httptest" "testing" + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/optimizely/agent/pkg/middleware" "github.com/optimizely/agent/pkg/optimizely" "github.com/optimizely/agent/pkg/optimizely/optimizelytest" - "github.com/optimizely/go-sdk/pkg/client" "github.com/optimizely/go-sdk/pkg/decide" "github.com/optimizely/go-sdk/pkg/entities" "github.com/optimizely/go-sdk/pkg/odp/segment" - - "github.com/go-chi/chi/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" ) type DecideTestSuite struct { diff --git a/pkg/handlers/get_datafile.go b/pkg/handlers/get_datafile.go index 368374ba..d458cb1b 100644 --- a/pkg/handlers/get_datafile.go +++ b/pkg/handlers/get_datafile.go @@ -34,11 +34,15 @@ func GetDatafile(w http.ResponseWriter, r *http.Request) { return } + logger := middleware.GetLogger(r) + datafile := optlyClient.GetOptimizelyConfig().GetDatafile() var raw map[string]interface{} if err = json.Unmarshal([]byte(datafile), &raw); err != nil { RenderError(err, http.StatusInternalServerError, w, r) return } + + logger.Info().Msg("Successfully returned datafile") render.JSON(w, r, raw) } diff --git a/pkg/handlers/lookup.go b/pkg/handlers/lookup.go index 0682d298..30298d2c 100644 --- a/pkg/handlers/lookup.go +++ b/pkg/handlers/lookup.go @@ -21,9 +21,9 @@ import ( "errors" "net/http" - "github.com/optimizely/agent/pkg/middleware" - "github.com/go-chi/render" + + "github.com/optimizely/agent/pkg/middleware" ) type lookupBody struct { @@ -47,6 +47,8 @@ func Lookup(w http.ResponseWriter, r *http.Request) { return } + logger := middleware.GetLogger(r) + if optlyClient.UserProfileService == nil { RenderError(ErrNoUPS, http.StatusInternalServerError, w, r) return @@ -75,5 +77,6 @@ func Lookup(w http.ResponseWriter, r *http.Request) { experimentBucketMap[k.ExperimentID] = map[string]interface{}{k.Field: v} } lookupResponse.ExperimentBucketMap = experimentBucketMap + logger.Info().Msgf("Looked up user profile for user %s", body.UserID) render.JSON(w, r, lookupResponse) } diff --git a/pkg/handlers/optimizely_config.go b/pkg/handlers/optimizely_config.go index 1659cbf5..5ad19a4d 100644 --- a/pkg/handlers/optimizely_config.go +++ b/pkg/handlers/optimizely_config.go @@ -33,6 +33,9 @@ func OptimizelyConfig(w http.ResponseWriter, r *http.Request) { return } + logger := middleware.GetLogger(r) + conf := optlyClient.GetOptimizelyConfig() + logger.Info().Msg("Successfully returned OptimizelyConfig") render.JSON(w, r, conf) } diff --git a/pkg/handlers/override.go b/pkg/handlers/override.go index 749bbf4b..36cb3ec4 100644 --- a/pkg/handlers/override.go +++ b/pkg/handlers/override.go @@ -61,7 +61,7 @@ func Override(w http.ResponseWriter, r *http.Request) { // Empty variation means remove if body.VariationKey == "" { - if override, err := optlyClient.RemoveForcedVariation(experimentKey, body.UserID); err != nil { + if override, err := optlyClient.RemoveForcedVariation(r.Context(), experimentKey, body.UserID); err != nil { RenderError(err, http.StatusInternalServerError, w, r) } else { render.JSON(w, r, override) @@ -69,10 +69,10 @@ func Override(w http.ResponseWriter, r *http.Request) { return } - logger.Debug().Str("experimentKey", experimentKey).Str("variationKey", body.VariationKey).Msg("setting override") - if override, err := optlyClient.SetForcedVariation(experimentKey, body.UserID, body.VariationKey); err != nil { + if override, err := optlyClient.SetForcedVariation(r.Context(), experimentKey, body.UserID, body.VariationKey); err != nil { RenderError(err, http.StatusInternalServerError, w, r) } else { + logger.Info().Str("experimentKey", experimentKey).Str("variationKey", body.VariationKey).Msg("Successfully set override") render.JSON(w, r, override) } } diff --git a/pkg/handlers/save.go b/pkg/handlers/save.go index 7d7903ce..2281aab7 100644 --- a/pkg/handlers/save.go +++ b/pkg/handlers/save.go @@ -20,10 +20,10 @@ package handlers import ( "net/http" + "github.com/go-chi/render" + "github.com/optimizely/agent/pkg/middleware" "github.com/optimizely/go-sdk/pkg/decision" - - "github.com/go-chi/render" ) type saveBody struct { @@ -38,6 +38,8 @@ func Save(w http.ResponseWriter, r *http.Request) { return } + logger := middleware.GetLogger(r) + if optlyClient.UserProfileService == nil { RenderError(ErrNoUPS, http.StatusInternalServerError, w, r) return @@ -58,6 +60,7 @@ func Save(w http.ResponseWriter, r *http.Request) { convertedProfile := convertToUserProfile(body) optlyClient.UserProfileService.Save(convertedProfile) + logger.Info().Msgf("Saved user profile for user %s", body.UserID) render.Status(r, http.StatusOK) } diff --git a/pkg/handlers/send_odp_event.go b/pkg/handlers/send_odp_event.go index d9fcfae3..91fdeb68 100644 --- a/pkg/handlers/send_odp_event.go +++ b/pkg/handlers/send_odp_event.go @@ -22,10 +22,10 @@ import ( "net/http" "github.com/go-chi/render" - "github.com/optimizely/go-sdk/pkg/odp/event" "github.com/optimizely/agent/pkg/middleware" "github.com/optimizely/agent/pkg/optimizely" + "github.com/optimizely/go-sdk/pkg/odp/event" ) // SendOdpEvent sends event to ODP platform @@ -36,6 +36,8 @@ func SendOdpEvent(w http.ResponseWriter, r *http.Request) { return } + logger := middleware.GetLogger(r) + body, err := getRequestOdpEvent(r) if err != nil { RenderError(err, http.StatusBadRequest, w, r) @@ -52,6 +54,7 @@ func SendOdpEvent(w http.ResponseWriter, r *http.Request) { Success: true, } + logger.Info().Msg("Successfully sent event to ODP platform") render.JSON(w, r, returnResult) } diff --git a/pkg/handlers/track.go b/pkg/handlers/track.go index 8359ee42..010431df 100644 --- a/pkg/handlers/track.go +++ b/pkg/handlers/track.go @@ -22,9 +22,9 @@ import ( "net/http" "github.com/go-chi/render" - "github.com/optimizely/go-sdk/pkg/entities" "github.com/optimizely/agent/pkg/middleware" + "github.com/optimizely/go-sdk/pkg/entities" ) type trackBody struct { @@ -40,6 +40,7 @@ func TrackEvent(w http.ResponseWriter, r *http.Request) { RenderError(err, http.StatusInternalServerError, w, r) return } + logger := middleware.GetLogger(r) var body trackBody err = ParseRequestBody(r, &body) @@ -60,12 +61,12 @@ func TrackEvent(w http.ResponseWriter, r *http.Request) { Attributes: body.UserAttributes, } - track, err := optlyClient.TrackEvent(eventKey, uc, body.EventTags) + track, err := optlyClient.TrackEvent(r.Context(), eventKey, uc, body.EventTags) if err != nil { RenderError(err, http.StatusInternalServerError, w, r) return } - middleware.GetLogger(r).Debug().Str("eventKey", eventKey).Msg("tracking event") + logger.Info().Str("eventKey", eventKey).Msg("tracked event") render.JSON(w, r, track) } diff --git a/pkg/handlers/utils.go b/pkg/handlers/utils.go index 82605cc7..e632fb1c 100644 --- a/pkg/handlers/utils.go +++ b/pkg/handlers/utils.go @@ -35,7 +35,7 @@ type ErrorResponse struct { // RenderError sets the request status and renders the error message. func RenderError(err error, status int, w http.ResponseWriter, r *http.Request) { - middleware.GetLogger(r).Info().Err(err).Int("status", status).Msg("render error") + middleware.GetLogger(r).Err(err).Int("status", status).Msg("render error") render.Status(r, status) render.JSON(w, r, ErrorResponse{Error: err.Error()}) } @@ -44,22 +44,24 @@ func RenderError(err error, status int, w http.ResponseWriter, r *http.Request) // into the provided interface. Note that we're sanitizing the returned error // so that it is not leaked back to the requestor. func ParseRequestBody(r *http.Request, v interface{}) error { + logger := middleware.GetLogger(r) + body, err := io.ReadAll(r.Body) if err != nil { msg := "error reading request body" - middleware.GetLogger(r).Error().Err(err).Msg(msg) + logger.Err(err).Msg(msg) return fmt.Errorf("%s", msg) } if len(body) == 0 { - middleware.GetLogger(r).Debug().Msg("body was empty skip JSON unmarshal") + logger.Info().Msg("body was empty skip JSON unmarshal") return nil } err = json.Unmarshal(body, &v) if err != nil { msg := "error parsing request body" - middleware.GetLogger(r).Error().Err(err).Msg(msg) + logger.Err(err).Msg(msg) return fmt.Errorf("%s", msg) } diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index cd8379ce..09c15ea3 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -18,11 +18,17 @@ package metrics import ( + "expvar" + "net/http" + "regexp" + "strings" "sync" go_kit_metrics "github.com/go-kit/kit/metrics" - go_kit_expvar "github.com/go-kit/kit/metrics/expvar" + go_kit_prometheus "github.com/go-kit/kit/metrics/prometheus" + stdprometheus "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog/log" ) @@ -33,12 +39,42 @@ const ( TimerPrefix = "timer" ) +const ( + prometheusPackage = "prometheus" +) + +// GetHandler returns request handler for provided metrics package type +func GetHandler(packageType string) http.Handler { + switch packageType { + case prometheusPackage: + return promhttp.Handler() + default: + // expvar + return expvar.Handler() + } +} + +// Timer is the collection of some timers +type Timer struct { + hits go_kit_metrics.Counter + totalTime go_kit_metrics.Counter + histogram go_kit_metrics.Histogram +} + +// Update timer components +func (t *Timer) Update(delta float64) { + t.hits.Add(1) + t.totalTime.Add(delta) + t.histogram.Observe(delta) +} + // Registry initializes expvar metrics registry type Registry struct { metricsCounterVars map[string]go_kit_metrics.Counter metricsGaugeVars map[string]go_kit_metrics.Gauge metricsHistogramVars map[string]go_kit_metrics.Histogram metricsTimerVars map[string]*Timer + metricsType string gaugeLock sync.RWMutex counterLock sync.RWMutex @@ -46,24 +82,6 @@ type Registry struct { timerLock sync.RWMutex } -// NewRegistry initializes metrics registry -func NewRegistry() *Registry { - - return &Registry{ - metricsCounterVars: map[string]go_kit_metrics.Counter{}, - metricsGaugeVars: map[string]go_kit_metrics.Gauge{}, - metricsHistogramVars: map[string]go_kit_metrics.Histogram{}, - metricsTimerVars: map[string]*Timer{}, - } -} - -// Timer is the collection of some timers -type Timer struct { - hits go_kit_metrics.Counter - totalTime go_kit_metrics.Counter - histogram go_kit_metrics.Histogram -} - // NewTimer constructs Timer func (m *Registry) NewTimer(key string) *Timer { if key == "" { @@ -81,16 +99,22 @@ func (m *Registry) NewTimer(key string) *Timer { return m.createTimer(combinedKey) } -// Update timer components -func (t *Timer) Update(delta float64) { - t.hits.Add(1) - t.totalTime.Add(delta) - t.histogram.Observe(delta) +// GetCounter gets go-kit expvar Counter +// NewRegistry initializes metrics registry +func NewRegistry(metricsType string) *Registry { + + registry := &Registry{ + metricsCounterVars: map[string]go_kit_metrics.Counter{}, + metricsGaugeVars: map[string]go_kit_metrics.Gauge{}, + metricsHistogramVars: map[string]go_kit_metrics.Histogram{}, + metricsTimerVars: map[string]*Timer{}, + metricsType: metricsType, + } + return registry } -// GetCounter gets go-kit expvar Counter +// GetCounter gets go-kit Counter func (m *Registry) GetCounter(key string) go_kit_metrics.Counter { - if key == "" { log.Warn().Msg("metrics counter key is empty") return nil @@ -103,13 +127,11 @@ func (m *Registry) GetCounter(key string) go_kit_metrics.Counter { if val, ok := m.metricsCounterVars[combinedKey]; ok { return val } - return m.createCounter(combinedKey) } -// GetGauge gets go-kit expvar Gauge +// GetGauge gets go-kit Gauge func (m *Registry) GetGauge(key string) go_kit_metrics.Gauge { - if key == "" { log.Warn().Msg("metrics gauge key is empty") return nil @@ -127,9 +149,8 @@ func (m *Registry) GetGauge(key string) go_kit_metrics.Gauge { // GetHistogram gets go-kit Histogram func (m *Registry) GetHistogram(key string) go_kit_metrics.Histogram { - if key == "" { - log.Warn().Msg("metrics gauge key is empty") + log.Warn().Msg("metrics histogram key is empty") return nil } @@ -141,25 +162,52 @@ func (m *Registry) GetHistogram(key string) go_kit_metrics.Histogram { return m.createHistogram(key) } -func (m *Registry) createGauge(key string) *go_kit_expvar.Gauge { - gaugeVar := go_kit_expvar.NewGauge(key) +func (m *Registry) createGauge(key string) (gaugeVar go_kit_metrics.Gauge) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + gaugeVar = go_kit_prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + gaugeVar = go_kit_expvar.NewGauge(name) + } m.metricsGaugeVars[key] = gaugeVar - return gaugeVar - + return } -func (m *Registry) createCounter(key string) *go_kit_expvar.Counter { - counterVar := go_kit_expvar.NewCounter(key) +func (m *Registry) createCounter(key string) (counterVar go_kit_metrics.Counter) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + counterVar = go_kit_prometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + counterVar = go_kit_expvar.NewCounter(name) + } m.metricsCounterVars[key] = counterVar - return counterVar - + return } -func (m *Registry) createHistogram(key string) *go_kit_expvar.Histogram { - histogramVar := go_kit_expvar.NewHistogram(key, 50) +func (m *Registry) createHistogram(key string) (histogramVar go_kit_metrics.Histogram) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + histogramVar = go_kit_prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + histogramVar = go_kit_expvar.NewHistogram(name, 50) + } m.metricsHistogramVars[key] = histogramVar - return histogramVar - + return } func (m *Registry) createTimer(key string) *Timer { @@ -168,8 +216,33 @@ func (m *Registry) createTimer(key string) *Timer { totalTime: m.createCounter(key + ".responseTime"), histogram: m.createHistogram(key + ".responseTimeHist"), } - m.metricsTimerVars[key] = timerVar return timerVar +} +// getPackageSupportedName converts name to package supported type +func (m *Registry) getPackageSupportedName(name string) string { + switch m.metricsType { + case prometheusPackage: + // https://prometheus.io/docs/practices/naming/ + return toSnakeCase(name) + default: + // Default expvar + return name + } +} + +func toSnakeCase(name string) string { + v := strings.Replace(name, "-", "_", -1) + strArray := strings.Split(v, ".") + var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") + var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + convertedArray := []string{} + + for _, v := range strArray { + snake := matchFirstCap.ReplaceAllString(v, "${1}_${2}") + snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") + convertedArray = append(convertedArray, strings.ToLower(snake)) + } + return strings.Join(convertedArray, "_") } diff --git a/pkg/metrics/metrics_prometheus_test.go b/pkg/metrics/metrics_prometheus_test.go new file mode 100644 index 00000000..a8a4cc10 --- /dev/null +++ b/pkg/metrics/metrics_prometheus_test.go @@ -0,0 +1,204 @@ +/**************************************************************************** + * Copyright 2023, Optimizely, Inc. and contributors * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + ***************************************************************************/ + +// Package optimizely // +package metrics + +import ( + "expvar" + "io/ioutil" + "net/http/httptest" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/stretchr/testify/assert" +) + +func TestPrometheusCounterValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + counter := metricsRegistry.GetCounter("metrics") + counter.Add(12) + counter.Add(23) + + expvar.Handler().ServeHTTP(rec, req) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "counter_metrics 35") +} + +func TestPrometheusCounterMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + counterKey := "next_counter_metrics" + counter := metricsRegistry.GetCounter(counterKey) + counter.Add(12) + + nextCounter := metricsRegistry.GetCounter(counterKey) + nextCounter.Add(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "counter_"+counterKey+"35") +} + +func TestPrometheusCounterEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + counter := metricsRegistry.GetCounter("") + assert.Nil(t, counter) +} + +func TestPrometheusGaugeValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + gauge := metricsRegistry.GetGauge("metrics") + gauge.Set(12) + gauge.Set(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "gauge_metrics") +} + +func TestPrometheusGaugeMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + guageKey := "next_gauge_metrics" + gauge := metricsRegistry.GetGauge(guageKey) + gauge.Set(12) + nextGauge := metricsRegistry.GetGauge(guageKey) + nextGauge.Set(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "gauge_"+guageKey+" 23") +} + +func TestPrometheusGaugeEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + gauge := metricsRegistry.GetGauge("") + assert.Nil(t, gauge) +} + +func TestPrometheusHistogramValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + histogram := metricsRegistry.GetHistogram("metrics") + histogram.Observe(12) + histogram.Observe(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") +} + +func TestPrometheusHistogramMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + histogramKey := "next_histogram_metrics" + histogram := metricsRegistry.GetHistogram(histogramKey) + histogram.Observe(12) + nextGauge := metricsRegistry.GetHistogram(histogramKey) + nextGauge.Observe(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") +} + +func TestPrometheusHistogramEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + histogram := metricsRegistry.GetHistogram("") + + assert.Nil(t, histogram) +} + +func TestPrometheusTimerValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + timer := metricsRegistry.NewTimer("metrics") + timer.Update(12) + timer.Update(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") + strings.Contains(strResponse, `timer_metrics_response_time_hist_bucket{le="+Inf"} 2`) +} + +func TestPrometheusTimerMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + timerKey := "next_timer_metrics" + timer := metricsRegistry.NewTimer(timerKey) + timer.Update(12) + nextTimer := metricsRegistry.NewTimer(timerKey) + nextTimer.Update(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") + strings.Contains(strResponse, `timer_metrics_response_time_hist_bucket{le="+Inf"} 2`) +} diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/metrics_test.go index 9b108798..1eadda22 100644 --- a/pkg/metrics/metrics_test.go +++ b/pkg/metrics/metrics_test.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019, Optimizely, Inc. and contributors * + * Copyright 2019,2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -28,12 +28,18 @@ import ( type JSON map[string]interface{} +func TestGetHandler(t *testing.T) { + assert.NotNil(t, GetHandler("")) + assert.NotNil(t, GetHandler("expvar")) + assert.NotNil(t, GetHandler("123131231")) +} + func TestCounterValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counter := metricsRegistry.GetCounter("metrics") counter.Add(12) counter.Add(23) @@ -52,7 +58,7 @@ func TestCounterMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counterKey := "next_counter_metrics" counter := metricsRegistry.GetCounter(counterKey) counter.Add(12) @@ -70,7 +76,7 @@ func TestCounterMultipleRetrievals(t *testing.T) { func TestCounterEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counter := metricsRegistry.GetCounter("") assert.Nil(t, counter) @@ -82,7 +88,7 @@ func TestGaugeValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") gauge := metricsRegistry.GetGauge("metrics") gauge.Set(12) gauge.Set(23) @@ -101,7 +107,7 @@ func TestGaugeMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") guageKey := "next_gauge_metrics" gauge := metricsRegistry.GetGauge(guageKey) gauge.Set(12) @@ -119,7 +125,7 @@ func TestGaugeMultipleRetrievals(t *testing.T) { func TestGaugeEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") gauge := metricsRegistry.GetGauge("") assert.Nil(t, gauge) @@ -131,7 +137,7 @@ func TestHistorgramValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogram := metricsRegistry.GetHistogram("metrics") histogram.Observe(12) histogram.Observe(23) @@ -151,7 +157,7 @@ func TestHistogramMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogramKey := "next_histogram_metrics" histogram := metricsRegistry.GetHistogram(histogramKey) histogram.Observe(12) @@ -170,7 +176,7 @@ func TestHistogramMultipleRetrievals(t *testing.T) { func TestHistogramEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogram := metricsRegistry.GetHistogram("") assert.Nil(t, histogram) @@ -181,7 +187,7 @@ func TestTimerValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") timer := metricsRegistry.NewTimer("metrics") timer.Update(12) timer.Update(23) @@ -202,7 +208,7 @@ func TestTimerMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") timerKey := "next_timer_metrics" timer := metricsRegistry.NewTimer(timerKey) timer.Update(12) @@ -219,3 +225,18 @@ func TestTimerMultipleRetrievals(t *testing.T) { assert.Equal(t, 23.0, expVarMap["timer.next_timer_metrics.responseTimeHist.p99"]) } + +func TestToSnakeCase(t *testing.T) { + assert.Equal(t, "", toSnakeCase("")) + assert.Equal(t, "abc", toSnakeCase("abc")) + assert.Equal(t, "abc_123", toSnakeCase("abc_123")) + assert.Equal(t, "abc_efg", toSnakeCase("abcEfg")) + assert.Equal(t, "timer_activate_response_time", toSnakeCase("timer.activate.responseTime")) + assert.Equal(t, "timer_activate_response_time_hist_p95", toSnakeCase("timer.activate.responseTimeHist.p95")) + assert.Equal(t, "timer_create_api_access_token_response_time_hist_p50", toSnakeCase("timer.create-api-access-token.responseTimeHist.p50")) + assert.Equal(t, "timer_get_config_response_time_hist_p50", toSnakeCase("timer.get-config.responseTimeHist.p50")) + assert.Equal(t, "timer_track_event_response_time_hist_p50", toSnakeCase("timer.track-event.responseTimeHist.p50")) + assert.Equal(t, "counter_dispatcher_success_flush", toSnakeCase("counter.dispatcher.successFlush")) + assert.Equal(t, "timer_get_config_response_time", toSnakeCase("timer.get-config.responseTime")) + assert.Equal(t, "timer_get_config_hits", toSnakeCase("timer.get-config.hits")) +} diff --git a/pkg/middleware/metrics_test.go b/pkg/middleware/metrics_test.go index 6148a4e8..5d35a3e2 100644 --- a/pkg/middleware/metrics_test.go +++ b/pkg/middleware/metrics_test.go @@ -53,7 +53,7 @@ func (rm *RequestMetrics) SetupRoute(key string) { r := httptest.NewRequest("GET", "/", nil) rm.req = r.WithContext(context.WithValue(r.Context(), responseTime, time.Now())) - rm.handler = http.Handler(Metricize(key, metrics.NewRegistry())(getTestMetrics())) + rm.handler = http.Handler(Metricize(key, metrics.NewRegistry(""))(getTestMetrics())) } diff --git a/pkg/middleware/trace.go b/pkg/middleware/trace.go new file mode 100644 index 00000000..e5270230 --- /dev/null +++ b/pkg/middleware/trace.go @@ -0,0 +1,58 @@ +/**************************************************************************** + * Copyright 2023 Optimizely, Inc. and contributors * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + ***************************************************************************/ + +// Package middleware // +package middleware + +import ( + "net/http" + + "github.com/go-chi/chi/v5/middleware" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +func AddTracing(tracerName, spanName string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + prop := otel.GetTextMapPropagator() + propCtx := prop.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) + + ctx, span := otel.Tracer(tracerName).Start(propCtx, spanName) + defer span.End() + + span.SetAttributes( + semconv.HTTPMethodKey.String(r.Method), + semconv.HTTPRouteKey.String(r.URL.Path), + semconv.HTTPURLKey.String(r.URL.String()), + semconv.HTTPHostKey.String(r.Host), + semconv.HTTPSchemeKey.String(r.URL.Scheme), + attribute.String(OptlySDKHeader, r.Header.Get(OptlySDKHeader)), + ) + + respWriter := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + + next.ServeHTTP(respWriter, r.WithContext(ctx)) + + span.SetAttributes( + semconv.HTTPStatusCodeKey.Int(respWriter.Status()), + ) + } + return http.HandlerFunc(fn) + } +} diff --git a/pkg/middleware/trace_test.go b/pkg/middleware/trace_test.go new file mode 100644 index 00000000..91e15e6d --- /dev/null +++ b/pkg/middleware/trace_test.go @@ -0,0 +1,51 @@ +/**************************************************************************** + * Copyright 2023 Optimizely, Inc. and contributors * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + ***************************************************************************/ + +// Package middleware // +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestAddTracing(t *testing.T) { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/text") + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + }) + + req := httptest.NewRequest("GET", "/", nil) + rr := httptest.NewRecorder() + middleware := http.Handler(AddTracing("test-tracer", "test-span")(handler)) + + // Serve the request through the middleware + middleware.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("Expected status code %v, but got %v", http.StatusOK, status) + } + + if body := rr.Body.String(); body != "OK" { + t.Errorf("Expected response body %v, but got %v", "OK", body) + } + + if typeHeader := rr.Header().Get("Content-Type"); typeHeader != "application/text" { + t.Errorf("Expected Content-Type header %v, but got %v", "application/text", typeHeader) + } +} diff --git a/pkg/middleware/utils.go b/pkg/middleware/utils.go index 24940c83..cf45b041 100644 --- a/pkg/middleware/utils.go +++ b/pkg/middleware/utils.go @@ -23,13 +23,13 @@ import ( "strconv" "strings" - "github.com/optimizely/agent/pkg/optimizely" - - "github.com/optimizely/go-sdk/pkg/config" - "github.com/go-chi/render" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "go.opentelemetry.io/otel/trace" + + "github.com/optimizely/agent/pkg/optimizely" + "github.com/optimizely/go-sdk/pkg/config" ) // ErrorResponse Model @@ -52,6 +52,12 @@ func GetLogger(r *http.Request) *zerolog.Logger { reqID := r.Header.Get(OptlyRequestHeader) logger := log.With().Str("requestId", reqID).Logger() + span := trace.SpanFromContext(r.Context()) + if span.SpanContext().TraceID().IsValid() { + logger = logger.With().Str("traceId", span.SpanContext().TraceID().String()).Logger() + logger = logger.With().Str("spanId", span.SpanContext().SpanID().String()).Logger() + } + if optimizely.ShouldIncludeSDKKey { sdkKey := r.Header.Get(OptlySDKHeader) sdkKeySplit := strings.Split(sdkKey, ":") diff --git a/pkg/middleware/utils_test.go b/pkg/middleware/utils_test.go index 8418e4a2..799477a3 100644 --- a/pkg/middleware/utils_test.go +++ b/pkg/middleware/utils_test.go @@ -21,13 +21,15 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" "github.com/optimizely/agent/pkg/optimizely" - "github.com/optimizely/go-sdk/pkg/config" ) @@ -54,15 +56,26 @@ func TestGetLogger(t *testing.T) { out := &bytes.Buffer{} req := httptest.NewRequest("GET", "/", nil) + traceId := "0af7651916cd43dd8448eb211c80319c" + spanId := "b9c7c989f97918e1" + req.Header.Set(OptlyRequestHeader, "12345") req.Header.Set(OptlySDKHeader, "some_key") - logger := GetLogger(req) + req.Header.Set("traceparent", fmt.Sprintf("00-%s-%s-01", traceId, spanId)) + + otel.SetTextMapPropagator(propagation.TraceContext{}) + ctx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.HeaderCarrier(req.Header)) + + logger := GetLogger(req.WithContext(ctx)) newLogger := logger.Output(out) newLogger.Info().Msg("some_message") assert.Contains(t, out.String(), `"requestId":"12345"`) assert.Contains(t, out.String(), `"sdkKey":"some_key"`) + assert.Contains(t, out.String(), `"traceId":"`+traceId+`"`) + assert.Contains(t, out.String(), `"spanId":"`+spanId+`"`) + optimizely.ShouldIncludeSDKKey = false out = &bytes.Buffer{} logger = GetLogger(req) @@ -70,6 +83,9 @@ func TestGetLogger(t *testing.T) { newLogger.Info().Msg("some_message") assert.Contains(t, out.String(), `"requestId":"12345"`) assert.NotContains(t, out.String(), `"sdkKey":"some_key"`) + + assert.NotContains(t, out.String(), `"traceId":"`+traceId+`"`) + assert.NotContains(t, out.String(), `"spanId":"`+spanId+`"`) } func TestGetFeature(t *testing.T) { diff --git a/pkg/optimizely/cache_test.go b/pkg/optimizely/cache_test.go index 398d644c..be7af2e1 100644 --- a/pkg/optimizely/cache_test.go +++ b/pkg/optimizely/cache_test.go @@ -104,7 +104,7 @@ func (suite *CacheTestSuite) TestUpdateConfigs() { } func (suite *CacheTestSuite) TestNewCache() { - agentMetricsRegistry := metrics.NewRegistry() + agentMetricsRegistry := metrics.NewRegistry("") sdkMetricsRegistry := NewRegistry(agentMetricsRegistry) // To improve coverage @@ -252,7 +252,7 @@ type DefaultLoaderTestSuite struct { func (s *DefaultLoaderTestSuite) SetupTest() { // Need the registry to be created only once since it panics if we create gauges with the same name again and again doOnce.Do(func() { - s.registry = &MetricsRegistry{metrics.NewRegistry()} + s.registry = &MetricsRegistry{metrics.NewRegistry("")} }) s.upsMap = cmap.New() s.bpFactory = func(options ...event.BPOptionConfig) *event.BatchEventProcessor { diff --git a/pkg/optimizely/client.go b/pkg/optimizely/client.go index 5e84e6db..3a3593bd 100644 --- a/pkg/optimizely/client.go +++ b/pkg/optimizely/client.go @@ -18,11 +18,14 @@ package optimizely import ( + "context" "errors" optimizelyclient "github.com/optimizely/go-sdk/pkg/client" "github.com/optimizely/go-sdk/pkg/decision" "github.com/optimizely/go-sdk/pkg/entities" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) // ErrEntityNotFound is returned when no entity exists with a given key @@ -81,7 +84,11 @@ func (c *OptlyClient) UpdateConfig() { } // TrackEvent checks for the existence of the event before calling the OptimizelyClient Track method -func (c *OptlyClient) TrackEvent(eventKey string, uc entities.UserContext, eventTags map[string]interface{}) (*Track, error) { +func (c *OptlyClient) TrackEvent(ctx context.Context, eventKey string, uc entities.UserContext, eventTags map[string]interface{}) (*Track, error) { + _, span := otel.Tracer("trackHandler").Start(ctx, "TrackEvent") + defer span.End() + span.SetAttributes(attribute.String("trackEventKey", eventKey)) + tr := &Track{ UserID: uc.ID, EventKey: eventKey, @@ -104,7 +111,10 @@ func (c *OptlyClient) TrackEvent(eventKey string, uc entities.UserContext, event // SetForcedVariation sets a forced variation for the argument experiment key and user ID // Returns false if the same forced variation was already set for the argument experiment and user, true otherwise // Returns an error when forced variations are not available on this OptlyClient instance -func (c *OptlyClient) SetForcedVariation(experimentKey, userID, variationKey string) (*Override, error) { +func (c *OptlyClient) SetForcedVariation(ctx context.Context, experimentKey, userID, variationKey string) (*Override, error) { + _, span := otel.Tracer("overrideHandler").Start(ctx, "SetForcedVariation") + defer span.End() + if c.ForcedVariations == nil { return &Override{}, ErrForcedVariationsUninitialized } @@ -139,12 +149,18 @@ func (c *OptlyClient) SetForcedVariation(experimentKey, userID, variationKey str override.Messages = messages } + span.SetAttributes(attribute.String("variationKey", override.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", override.ExperimentKey)) + c.ForcedVariations.SetVariation(forcedVariationKey, variationKey) return &override, nil } // RemoveForcedVariation removes any forced variation that was previously set for the argument experiment key and user ID -func (c *OptlyClient) RemoveForcedVariation(experimentKey, userID string) (*Override, error) { +func (c *OptlyClient) RemoveForcedVariation(ctx context.Context, experimentKey, userID string) (*Override, error) { + _, span := otel.Tracer("overrideHandler").Start(ctx, "RemoveForcedVariation") + defer span.End() + if c.ForcedVariations == nil { return &Override{}, ErrForcedVariationsUninitialized } @@ -171,12 +187,18 @@ func (c *OptlyClient) RemoveForcedVariation(experimentKey, userID string) (*Over override.Messages = messages c.ForcedVariations.RemoveVariation(forcedVariationKey) + span.SetAttributes(attribute.String("variationKey", override.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", override.ExperimentKey)) + return &override, nil } // ActivateFeature activates a feature for a given user by getting the feature enabled status and all // associated variables -func (c *OptlyClient) ActivateFeature(key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { +func (c *OptlyClient) ActivateFeature(ctx context.Context, key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { + _, span := otel.Tracer("activateHandler").Start(ctx, "ActivateFeature") + defer span.End() + unsafeDecisionInfo, err := c.GetDetailedFeatureDecisionUnsafe(key, uc, disableTracking) if err != nil { return &Decision{}, err @@ -192,11 +214,20 @@ func (c *OptlyClient) ActivateFeature(key string, uc entities.UserContext, disab VariationKey: unsafeDecisionInfo.VariationKey, } + span.SetAttributes(attribute.String("variationKey", dec.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", dec.ExperimentKey)) + span.SetAttributes(attribute.String("featureKey", dec.FeatureKey)) + span.SetAttributes(attribute.Bool("enabled", dec.Enabled)) + span.SetAttributes(attribute.String("type", dec.Type)) + return dec, nil } // ActivateExperiment activates an experiment -func (c *OptlyClient) ActivateExperiment(key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { +func (c *OptlyClient) ActivateExperiment(ctx context.Context, key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { + _, span := otel.Tracer("activateHandler").Start(ctx, "ActivateExperiment") + defer span.End() + var variation string var err error @@ -218,5 +249,10 @@ func (c *OptlyClient) ActivateExperiment(key string, uc entities.UserContext, di Variables: map[string]interface{}{}, } + span.SetAttributes(attribute.String("variationKey", dec.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", dec.ExperimentKey)) + span.SetAttributes(attribute.Bool("enabled", dec.Enabled)) + span.SetAttributes(attribute.String("type", dec.Type)) + return dec, nil } diff --git a/pkg/optimizely/client_test.go b/pkg/optimizely/client_test.go index 0bb347fe..090241f7 100644 --- a/pkg/optimizely/client_test.go +++ b/pkg/optimizely/client_test.go @@ -18,6 +18,7 @@ package optimizely import ( + "context" "fmt" "testing" @@ -65,7 +66,7 @@ func (suite *ClientTestSuite) TestTrackEvent() { eventKey := "eventKey" suite.testClient.AddEvent(entities.Event{Key: eventKey}) tags := map[string]interface{}{"tag": "value"} - actual, err := suite.optlyClient.TrackEvent(eventKey, suite.userContext, tags) + actual, err := suite.optlyClient.TrackEvent(context.Background(), eventKey, suite.userContext, tags) suite.NoError(err) expected := &Track{ @@ -122,7 +123,7 @@ func (suite *ClientTestSuite) TestValidSetForcedVariations() { userID := "testUser" for _, scenario := range scenarios { - actual, err := suite.optlyClient.SetForcedVariation(scenario.experimentKey, userID, scenario.variationKey) + actual, err := suite.optlyClient.SetForcedVariation(context.Background(), scenario.experimentKey, userID, scenario.variationKey) suite.NoError(err) expected := &Override{ @@ -161,10 +162,10 @@ func (suite *ClientTestSuite) TestRemoveForcedVariation() { } userID := "testUser" - _, _ = suite.optlyClient.SetForcedVariation(suite.featureExp.Key, userID, "enabled_var") + _, _ = suite.optlyClient.SetForcedVariation(context.Background(), suite.featureExp.Key, userID, "enabled_var") for _, scenario := range scenarios { - actual, err := suite.optlyClient.RemoveForcedVariation(suite.featureExp.Key, userID) + actual, err := suite.optlyClient.RemoveForcedVariation(context.Background(), suite.featureExp.Key, userID) suite.NoError(err) expected := &Override{ @@ -211,7 +212,7 @@ func (suite *ClientTestSuite) TestActivateFeature() { // Response should be the same regardless of the flag for _, flag := range []bool{true, false} { - actual, err := suite.optlyClient.ActivateFeature(feature.Key, entities.UserContext{ID: "testUser"}, flag) + actual, err := suite.optlyClient.ActivateFeature(context.Background(), feature.Key, entities.UserContext{ID: "testUser"}, flag) suite.NoError(err) suite.Equal(expected, actual) } @@ -237,7 +238,7 @@ func (suite *ClientTestSuite) TestActivateExperiment() { // Response should be the same regardless of the flag for _, flag := range []bool{true, false} { - actual, err := suite.optlyClient.ActivateExperiment(experiment.Key, entities.UserContext{ID: "testUser"}, flag) + actual, err := suite.optlyClient.ActivateExperiment(context.Background(), experiment.Key, entities.UserContext{ID: "testUser"}, flag) suite.NoError(err) suite.Equal(expected, actual) } @@ -314,7 +315,7 @@ func TestTrackErrorConfigManager(t *testing.T) { } uc := entities.UserContext{ID: "userId"} - actual, err := optlyClient.TrackEvent("something", uc, map[string]interface{}{}) + actual, err := optlyClient.TrackEvent(context.Background(), "something", uc, map[string]interface{}{}) assert.EqualError(t, err, "config error") expected := &Track{} @@ -341,7 +342,7 @@ func TestTrackErrorClient(t *testing.T) { } uc := entities.UserContext{ID: "userId"} - actual, err := optlyClient.TrackEvent("something", uc, map[string]interface{}{}) + actual, err := optlyClient.TrackEvent(context.Background(), "something", uc, map[string]interface{}{}) assert.NoError(t, err) expected := &Track{ diff --git a/pkg/optimizely/metrics_test.go b/pkg/optimizely/metrics_test.go index 76ce9c8b..b64ed154 100644 --- a/pkg/optimizely/metrics_test.go +++ b/pkg/optimizely/metrics_test.go @@ -35,7 +35,7 @@ func TestCounterValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := metrics.NewRegistry() + metricsRegistry := metrics.NewRegistry("") metricsSDKRegistry := NewRegistry(metricsRegistry) counter := metricsSDKRegistry.GetCounter("metrics") @@ -56,7 +56,7 @@ func TestGaugeValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := metrics.NewRegistry() + metricsRegistry := metrics.NewRegistry("") metricsSDKRegistry := NewRegistry(metricsRegistry) gauge := metricsSDKRegistry.GetGauge("metrics") diff --git a/pkg/routers/api.go b/pkg/routers/api.go index c2e513c8..b7d2aac5 100644 --- a/pkg/routers/api.go +++ b/pkg/routers/api.go @@ -64,7 +64,6 @@ func forbiddenHandler(message string) http.HandlerFunc { // NewDefaultAPIRouter creates a new router with the default backing optimizely.Cache func NewDefaultAPIRouter(optlyCache optimizely.Cache, conf config.APIConfig, metricsRegistry *metrics.Registry) http.Handler { - authProvider := middleware.NewAuth(&conf.Auth) if authProvider == nil { log.Error().Msg("unable to initialize api auth middleware.") @@ -134,6 +133,18 @@ func WithAPIRouter(opt *APIOptions, r chi.Router) { createAccesstokenTimer := middleware.Metricize("create-api-access-token", opt.metricsRegistry) contentTypeMiddleware := chimw.AllowContentType("application/json") + configTracer := middleware.AddTracing("configHandler", "OptimizelyConfig") + datafileTracer := middleware.AddTracing("datafileHandler", "OptimizelyDatafile") + activateTracer := middleware.AddTracing("activateHandler", "Activate") + decideTracer := middleware.AddTracing("decideHandler", "Decide") + trackTracer := middleware.AddTracing("trackHandler", "Track") + overrideTracer := middleware.AddTracing("overrideHandler", "Override") + lookupTracer := middleware.AddTracing("lookupHandler", "Lookup") + saveTracer := middleware.AddTracing("saveHandler", "Save") + sendOdpEventTracer := middleware.AddTracing("sendOdpEventHandler", "SendOdpEvent") + nStreamTracer := middleware.AddTracing("notificationHandler", "SendNotificationEvent") + authTracer := middleware.AddTracing("authHandler", "AuthToken") + if opt.maxConns > 0 { // Note this is NOT a rate limiter, but a concurrency threshold r.Use(chimw.Throttle(opt.maxConns)) @@ -144,19 +155,19 @@ func WithAPIRouter(opt *APIOptions, r chi.Router) { r.Route("/v1", func(r chi.Router) { r.Use(opt.corsHandler, opt.sdkMiddleware) - r.With(getConfigTimer, opt.oAuthMiddleware).Get("/config", opt.configHandler) - r.With(getDatafileTimer, opt.oAuthMiddleware).Get("/datafile", opt.datafileHandler) - r.With(activateTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/activate", opt.activateHandler) - r.With(decideTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/decide", opt.decideHandler) - r.With(trackTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/track", opt.trackHandler) - r.With(overrideTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/override", opt.overrideHandler) - r.With(lookupTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/lookup", opt.lookupHandler) - r.With(saveTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/save", opt.saveHandler) - r.With(sendOdpEventTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/send-odp-event", opt.sendOdpEventHandler) - r.With(opt.oAuthMiddleware).Get("/notifications/event-stream", opt.nStreamHandler) + r.With(getConfigTimer, opt.oAuthMiddleware, configTracer).Get("/config", opt.configHandler) + r.With(getDatafileTimer, opt.oAuthMiddleware, datafileTracer).Get("/datafile", opt.datafileHandler) + r.With(activateTimer, opt.oAuthMiddleware, contentTypeMiddleware, activateTracer).Post("/activate", opt.activateHandler) + r.With(decideTimer, opt.oAuthMiddleware, contentTypeMiddleware, decideTracer).Post("/decide", opt.decideHandler) + r.With(trackTimer, opt.oAuthMiddleware, contentTypeMiddleware, trackTracer).Post("/track", opt.trackHandler) + r.With(overrideTimer, opt.oAuthMiddleware, contentTypeMiddleware, overrideTracer).Post("/override", opt.overrideHandler) + r.With(lookupTimer, opt.oAuthMiddleware, contentTypeMiddleware, lookupTracer).Post("/lookup", opt.lookupHandler) + r.With(saveTimer, opt.oAuthMiddleware, contentTypeMiddleware, saveTracer).Post("/save", opt.saveHandler) + r.With(sendOdpEventTimer, opt.oAuthMiddleware, contentTypeMiddleware, sendOdpEventTracer).Post("/send-odp-event", opt.sendOdpEventHandler) + r.With(opt.oAuthMiddleware, nStreamTracer).Get("/notifications/event-stream", opt.nStreamHandler) }) - r.With(createAccesstokenTimer).Post("/oauth/token", opt.oAuthHandler) + r.With(createAccesstokenTimer, authTracer).Post("/oauth/token", opt.oAuthHandler) statikFS, err := fs.New() if err != nil { diff --git a/pkg/routers/api_test.go b/pkg/routers/api_test.go index 2f0fb84f..bc7776e9 100644 --- a/pkg/routers/api_test.go +++ b/pkg/routers/api_test.go @@ -33,7 +33,7 @@ import ( "github.com/optimizely/agent/pkg/optimizely/optimizelytest" ) -var metricsRegistry = metrics.NewRegistry() +var metricsRegistry = metrics.NewRegistry("") const methodHeaderKey = "X-Method-Header" const clientHeaderKey = "X-Client-Header" @@ -373,8 +373,7 @@ func TestNewDefaultClientRouterInvalidMiddlewareConfig(t *testing.T) { } func TestForbiddenRoutes(t *testing.T) { - conf := config.APIConfig{} - mux := NewDefaultAPIRouter(MockCache{}, conf, metricsRegistry) + mux := NewDefaultAPIRouter(MockCache{}, config.APIConfig{}, metricsRegistry) routes := []struct { method string