From bfffc2f4c7e5102287e8d09a4aeb1ef9b1868f51 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 16 May 2024 17:01:43 -0700 Subject: [PATCH 01/39] HasLambdaEnv more public --- internal/config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index c9ef73d0..34a99cd9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -340,8 +340,8 @@ const ( may be different from your setting.` ) -// hasLambdaEnv checks if the AWS Lambda env var is set. -func hasLambdaEnv() bool { +// HasLambdaEnv checks if the AWS Lambda env var is set. +func HasLambdaEnv() bool { return os.Getenv("AWS_LAMBDA_FUNCTION_NAME") != "" && os.Getenv("LAMBDA_TASK_ROOT") != "" } @@ -362,12 +362,12 @@ func (c *Config) validate() error { c.Ec2MetadataTimeout = t } - if c.TransactionName != "" && !hasLambdaEnv() { + if c.TransactionName != "" && !HasLambdaEnv() { log.Info(InvalidEnv("TransactionName", c.TransactionName)) c.TransactionName = getFieldDefaultValue(c, "TransactionName") } - if !hasLambdaEnv() { + if !HasLambdaEnv() { if c.ServiceKey != "" { c.ServiceKey = ToServiceKey(c.ServiceKey) if ok := IsValidServiceKey(c.ServiceKey); !ok { From 55bae2e1a42b34ead2e4c7942032acc8d4f6f91e Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 16 May 2024 17:03:00 -0700 Subject: [PATCH 02/39] WIP add UpdateSettingFromFile called by agent ticker --- internal/oboe/oboe.go | 50 +++++++++++++++++++++++++++++++++++++++---- swo/agent.go | 22 +++++++++++++++---- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 038683bc..184c5429 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -18,16 +18,17 @@ import ( "encoding/binary" "errors" "fmt" + "math" + "strings" + "sync" + "time" + "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/constants" "github.com/solarwinds/apm-go/internal/log" "github.com/solarwinds/apm-go/internal/metrics" "github.com/solarwinds/apm-go/internal/rand" "github.com/solarwinds/apm-go/internal/w3cfmt" - "math" - "strings" - "sync" - "time" ) const ( @@ -48,6 +49,7 @@ const ( type Oboe interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) + UpdateSettingFromFile() CheckSettingsTimeout() GetSetting() (*settings, bool) RemoveSetting() @@ -250,6 +252,46 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 o.Unlock() } +func (o *oboe) UpdateSettingFromFile() { + ns := newOboeSettings() + + var sType int = 1 // always DEFAULT_SAMPLE_RATE + var layer string = "" // not set since type is always DEFAULT_SAMPLE_RATE + + ns.source = settingType(sType).toSampleSource() + ns.layer = layer + + // TODO get these from json file; placeholders from example file for now + ns.timestamp = time.Now() + ns.flags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") + ns.originalFlags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") + ns.value = adjustSampleRate(1000000) + ns.ttl = 120 + + rate := float64(0.374) + capacity := float64(6.8) + ns.bucket.setRateCap(rate, capacity) + + tRelaxedRate := float64(1) + tRelaxedCapacity := float64(20) + ns.triggerTraceRelaxedBucket.setRateCap(tRelaxedRate, tRelaxedCapacity) + + tStrictRate := float64(0.1) + tStrictCapacity := float64(6) + ns.triggerTraceStrictBucket.setRateCap(tStrictRate, tStrictCapacity) + + merged := mergeLocalSetting(ns) + + key := settingKey{ + sType: settingType(sType), + layer: layer, + } + + o.Lock() + o.settings[key] = merged + o.Unlock() +} + // CheckSettingsTimeout checks and deletes expired settings func (o *oboe) CheckSettingsTimeout() { o.checkSettingsTimeout() diff --git a/swo/agent.go b/swo/agent.go index 5500e7a6..99dc9f33 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -16,6 +16,11 @@ package swo import ( "context" + "io" + stdlog "log" + "strings" + "time" + "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/entryspans" "github.com/solarwinds/apm-go/internal/exporter" @@ -32,9 +37,6 @@ import ( "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" - "io" - stdlog "log" - "strings" "github.com/pkg/errors" ) @@ -88,8 +90,16 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { // return a no-op func so that we don't cause a nil-deref for the end-user }, err } + config.Load() // earlier so we can call HasLambdaEnv earlier registry := metrics.NewLegacyRegistry() o := oboe.NewOboe() + + // TODO only if in lambda + settingsTicker := time.NewTicker(30 * time.Second) + for range settingsTicker.C { + o.UpdateSettingFromFile() + } + _reporter, err := reporter.Start(resrc, registry, o) if err != nil { return func() {}, err @@ -100,7 +110,6 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { if err != nil { return func() {}, err } - config.Load() isAppoptics := strings.Contains(strings.ToLower(config.GetCollector()), "appoptics.com") proc := processor.NewInboundMetricsSpanProcessor(registry, isAppoptics) prop := propagation.NewCompositeTextMapPropagator( @@ -117,6 +126,11 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { ) otel.SetTracerProvider(tp) return func() { + // stop ticker + // TODO only if in lambda + settingsTicker.Stop() + + // shut down TracerProvider, and log error if issues if err := tp.Shutdown(context.Background()); err != nil { stdlog.Fatal(err) } From 5f6e257117da86bff1918d535129bb18e84ba1b2 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 17 May 2024 13:57:35 -0700 Subject: [PATCH 03/39] Move ticker to Oboe --- internal/oboe/oboe.go | 16 +++++++++++++++- swo/agent.go | 8 ++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 184c5429..23b84c45 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -50,6 +50,8 @@ const ( type Oboe interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) UpdateSettingFromFile() + StartSettingTicker() + StopSettingTicker() CheckSettingsTimeout() GetSetting() (*settings, bool) RemoveSetting() @@ -67,7 +69,8 @@ func NewOboe() Oboe { type oboe struct { sync.RWMutex - settings map[settingKey]*settings + settings map[settingKey]*settings + settingsTicker *time.Ticker } var _ Oboe = &oboe{} @@ -292,6 +295,17 @@ func (o *oboe) UpdateSettingFromFile() { o.Unlock() } +func (o *oboe) StartSettingTicker() { + o.settingsTicker = time.NewTicker(10 * time.Second) + for range o.settingsTicker.C { + o.UpdateSettingFromFile() + } +} + +func (o *oboe) StopSettingTicker() { + o.settingsTicker.Stop() +} + // CheckSettingsTimeout checks and deletes expired settings func (o *oboe) CheckSettingsTimeout() { o.checkSettingsTimeout() diff --git a/swo/agent.go b/swo/agent.go index 99dc9f33..af49964a 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -19,7 +19,6 @@ import ( "io" stdlog "log" "strings" - "time" "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/entryspans" @@ -95,10 +94,7 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { o := oboe.NewOboe() // TODO only if in lambda - settingsTicker := time.NewTicker(30 * time.Second) - for range settingsTicker.C { - o.UpdateSettingFromFile() - } + go o.StartSettingTicker() _reporter, err := reporter.Start(resrc, registry, o) if err != nil { @@ -128,7 +124,7 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { return func() { // stop ticker // TODO only if in lambda - settingsTicker.Stop() + o.StopSettingTicker() // shut down TracerProvider, and log error if issues if err := tp.Shutdown(context.Background()); err != nil { From 67d2f5cb9fcd15f81ea27197f97263543d2f0742 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 17 May 2024 17:14:26 -0700 Subject: [PATCH 04/39] Add settingLambda to unmarshall settings json --- internal/oboe/oboe.go | 23 ++++++++++++++++++++- internal/oboe/settings_lambda.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 internal/oboe/settings_lambda.go diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 23b84c45..3109a246 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -16,9 +16,13 @@ package oboe import ( "encoding/binary" + "encoding/json" "errors" "fmt" + "io" + stdlog "log" "math" + "os" "strings" "sync" "time" @@ -258,13 +262,30 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 func (o *oboe) UpdateSettingFromFile() { ns := newOboeSettings() + // TODO tracing mode disabled if cannot open/parse/get all settings + settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") + if err != nil { + stdlog.Fatal("Could not open Lambda settings file") + } + settingBytes, err := io.ReadAll(settingFile) + if err != nil { + stdlog.Fatal("Could not read Lambda settings file") + } + // Settings file is an array with a single settings object + var settingLambdas []settingLambda + json.Unmarshal(settingBytes, &settingLambdas) + var settingLambda settingLambda = settingLambdas[0] + // debug + stdlog.Printf("settingLambda: %v", settingLambda) + stdlog.Printf("settingLambda.Arguments: %v", settingLambda.Arguments) + var sType int = 1 // always DEFAULT_SAMPLE_RATE var layer string = "" // not set since type is always DEFAULT_SAMPLE_RATE ns.source = settingType(sType).toSampleSource() ns.layer = layer - // TODO get these from json file; placeholders from example file for now + // TODO get these from settingLambda; placeholders from example file for now ns.timestamp = time.Now() ns.flags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") ns.originalFlags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go new file mode 100644 index 00000000..a2e9f58d --- /dev/null +++ b/internal/oboe/settings_lambda.go @@ -0,0 +1,35 @@ +// © 2023 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 oboe + +type settingLambda struct { + Arguments *settingArguments `json:"arguments"` + Flags string `json:"flags"` + Layer string `json:"layer"` + Timestamp int `json:"timestamp"` + Ttl int64 `json:"ttl"` + Stype int `json:"type"` + Value int `json:"value"` +} + +type settingArguments struct { + BucketCapacity float64 `json:"BucketCapacity"` + BucketRate float64 `json:"BucketRate"` + MetricsFlushInterval int64 `json:"MetricsFlushInterval"` + TriggerRelaxedBucketCapacity float64 `json:"TriggerRelaxedBucketCapacity"` + TriggerRelaxedBucketRate float64 `json:"TriggerRelaxedBucketRate"` + TriggerStrictBucketCapacity float64 `json:"TriggerStrictBucketCapacity"` + TriggerStrictBucketRate float64 `json:"TriggerStrictBucketRate"` +} From 7bd3ab423ff0fa1175546ac5fa0aab961e21a9d6 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 17 May 2024 17:27:28 -0700 Subject: [PATCH 05/39] UpdateSettingFromFile uses JSON to merge settings --- internal/oboe/oboe.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 3109a246..883e8a2e 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -285,23 +285,22 @@ func (o *oboe) UpdateSettingFromFile() { ns.source = settingType(sType).toSampleSource() ns.layer = layer - // TODO get these from settingLambda; placeholders from example file for now - ns.timestamp = time.Now() - ns.flags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") - ns.originalFlags = flagStringToBin("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE") - ns.value = adjustSampleRate(1000000) - ns.ttl = 120 - - rate := float64(0.374) - capacity := float64(6.8) + ns.timestamp = time.Unix(int64(settingLambda.Timestamp), 0) + ns.flags = flagStringToBin(settingLambda.Flags) + ns.originalFlags = flagStringToBin(settingLambda.Flags) + ns.value = adjustSampleRate(int64(settingLambda.Value)) + ns.ttl = int64(settingLambda.Ttl) + + rate := float64(settingLambda.Arguments.BucketRate) + capacity := float64(settingLambda.Arguments.BucketCapacity) ns.bucket.setRateCap(rate, capacity) - tRelaxedRate := float64(1) - tRelaxedCapacity := float64(20) + tRelaxedRate := float64(settingLambda.Arguments.TriggerRelaxedBucketRate) + tRelaxedCapacity := float64(settingLambda.Arguments.TriggerRelaxedBucketCapacity) ns.triggerTraceRelaxedBucket.setRateCap(tRelaxedRate, tRelaxedCapacity) - tStrictRate := float64(0.1) - tStrictCapacity := float64(6) + tStrictRate := float64(settingLambda.Arguments.TriggerStrictBucketRate) + tStrictCapacity := float64(settingLambda.Arguments.TriggerStrictBucketCapacity) ns.triggerTraceStrictBucket.setRateCap(tStrictRate, tStrictCapacity) merged := mergeLocalSetting(ns) From fb8c8e3f8dc343f8b63412ee1faf773314563d00 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 17 May 2024 18:23:23 -0700 Subject: [PATCH 06/39] Add newSettingLambdaFromFile --- internal/oboe/oboe.go | 23 ++++----------------- internal/oboe/settings_lambda.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 883e8a2e..8bdacce0 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -16,13 +16,10 @@ package oboe import ( "encoding/binary" - "encoding/json" "errors" "fmt" - "io" stdlog "log" "math" - "os" "strings" "sync" "time" @@ -260,28 +257,16 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 } func (o *oboe) UpdateSettingFromFile() { - ns := newOboeSettings() - - // TODO tracing mode disabled if cannot open/parse/get all settings - settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") + settingLambda, err := newSettingLambdaFromFile() if err != nil { - stdlog.Fatal("Could not open Lambda settings file") + stdlog.Fatalf("Could not update setting from file: %s", err) + return } - settingBytes, err := io.ReadAll(settingFile) - if err != nil { - stdlog.Fatal("Could not read Lambda settings file") - } - // Settings file is an array with a single settings object - var settingLambdas []settingLambda - json.Unmarshal(settingBytes, &settingLambdas) - var settingLambda settingLambda = settingLambdas[0] - // debug - stdlog.Printf("settingLambda: %v", settingLambda) - stdlog.Printf("settingLambda.Arguments: %v", settingLambda.Arguments) var sType int = 1 // always DEFAULT_SAMPLE_RATE var layer string = "" // not set since type is always DEFAULT_SAMPLE_RATE + ns := newOboeSettings() ns.source = settingType(sType).toSampleSource() ns.layer = layer diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index a2e9f58d..b1e637a7 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -14,6 +14,14 @@ package oboe +import ( + "encoding/json" + "errors" + "io" + stdlog "log" + "os" +) + type settingLambda struct { Arguments *settingArguments `json:"arguments"` Flags string `json:"flags"` @@ -33,3 +41,30 @@ type settingArguments struct { TriggerStrictBucketCapacity float64 `json:"TriggerStrictBucketCapacity"` TriggerStrictBucketRate float64 `json:"TriggerStrictBucketRate"` } + +func newSettingLambdaFromFile() (*settingLambda, error) { + // TODO tracing mode disabled if cannot open/parse/get all settings + settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") + if err != nil { + return nil, err + } + settingBytes, err := io.ReadAll(settingFile) + if err != nil { + return nil, err + } + // Settings file should be an array with a single settings object + var settingLambdas []settingLambda + if err := json.Unmarshal(settingBytes, &settingLambdas); err != nil { + return nil, err + } + if len(settingLambdas) != 1 { + return nil, errors.New("settings file is incorrectly formatted") + } + + var settingLambda settingLambda = settingLambdas[0] + // tmp: debug + stdlog.Printf("settingLambda: %v", settingLambda) + stdlog.Printf("settingLambda.Arguments: %v", settingLambda.Arguments) + + return &settingLambda, nil +} From d9156ffda14565b8296430b1f24ea975eae70704 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 17 May 2024 18:37:57 -0700 Subject: [PATCH 07/39] UpdateSettingFromFile returns bool for success --- internal/oboe/oboe.go | 9 ++++++--- internal/oboe/settings_lambda.go | 1 - swo/agent.go | 10 +++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 8bdacce0..ae026b36 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -50,7 +50,7 @@ const ( type Oboe interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) - UpdateSettingFromFile() + UpdateSettingFromFile() bool StartSettingTicker() StopSettingTicker() CheckSettingsTimeout() @@ -256,11 +256,12 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 o.Unlock() } -func (o *oboe) UpdateSettingFromFile() { +// UpdateSettingFromFile returns bool if reading file and update successful +func (o *oboe) UpdateSettingFromFile() bool { settingLambda, err := newSettingLambdaFromFile() if err != nil { stdlog.Fatalf("Could not update setting from file: %s", err) - return + return false } var sType int = 1 // always DEFAULT_SAMPLE_RATE @@ -298,6 +299,8 @@ func (o *oboe) UpdateSettingFromFile() { o.Lock() o.settings[key] = merged o.Unlock() + + return true } func (o *oboe) StartSettingTicker() { diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index b1e637a7..5fb903de 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -43,7 +43,6 @@ type settingArguments struct { } func newSettingLambdaFromFile() (*settingLambda, error) { - // TODO tracing mode disabled if cannot open/parse/get all settings settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") if err != nil { return nil, err diff --git a/swo/agent.go b/swo/agent.go index af49964a..6d51820f 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -94,7 +94,15 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { o := oboe.NewOboe() // TODO only if in lambda - go o.StartSettingTicker() + // Attempt to get lambda settings first time; disable tracing if error + ok := o.UpdateSettingFromFile() + if !ok { + // TODO tracing mode disabled if cannot open/parse/get all settings + stdlog.Print("TODO: could not update setting from file; tracing disabled") + } else { + // start regular settings file reading + go o.StartSettingTicker() + } _reporter, err := reporter.Start(resrc, registry, o) if err != nil { From 331c73c009a8bca310ff095bba3bc4ceccd06a89 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 11:55:24 -0700 Subject: [PATCH 08/39] Revert "UpdateSettingFromFile returns bool for success" This reverts commit d9156ffda14565b8296430b1f24ea975eae70704. --- internal/oboe/oboe.go | 9 +++------ internal/oboe/settings_lambda.go | 1 + swo/agent.go | 10 +--------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index ae026b36..8bdacce0 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -50,7 +50,7 @@ const ( type Oboe interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) - UpdateSettingFromFile() bool + UpdateSettingFromFile() StartSettingTicker() StopSettingTicker() CheckSettingsTimeout() @@ -256,12 +256,11 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 o.Unlock() } -// UpdateSettingFromFile returns bool if reading file and update successful -func (o *oboe) UpdateSettingFromFile() bool { +func (o *oboe) UpdateSettingFromFile() { settingLambda, err := newSettingLambdaFromFile() if err != nil { stdlog.Fatalf("Could not update setting from file: %s", err) - return false + return } var sType int = 1 // always DEFAULT_SAMPLE_RATE @@ -299,8 +298,6 @@ func (o *oboe) UpdateSettingFromFile() bool { o.Lock() o.settings[key] = merged o.Unlock() - - return true } func (o *oboe) StartSettingTicker() { diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 5fb903de..b1e637a7 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -43,6 +43,7 @@ type settingArguments struct { } func newSettingLambdaFromFile() (*settingLambda, error) { + // TODO tracing mode disabled if cannot open/parse/get all settings settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") if err != nil { return nil, err diff --git a/swo/agent.go b/swo/agent.go index 6d51820f..af49964a 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -94,15 +94,7 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { o := oboe.NewOboe() // TODO only if in lambda - // Attempt to get lambda settings first time; disable tracing if error - ok := o.UpdateSettingFromFile() - if !ok { - // TODO tracing mode disabled if cannot open/parse/get all settings - stdlog.Print("TODO: could not update setting from file; tracing disabled") - } else { - // start regular settings file reading - go o.StartSettingTicker() - } + go o.StartSettingTicker() _reporter, err := reporter.Start(resrc, registry, o) if err != nil { From cd1632deaf173ed8d108c07ef2de9b1ef709654a Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 12:45:05 -0700 Subject: [PATCH 09/39] UpdateSettingFromFile uses UpdateSetting --- internal/oboe/oboe.go | 44 +++++------------------ internal/oboe/settings_lambda.go | 60 ++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 8bdacce0..f8d8e959 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -262,42 +262,14 @@ func (o *oboe) UpdateSettingFromFile() { stdlog.Fatalf("Could not update setting from file: %s", err) return } - - var sType int = 1 // always DEFAULT_SAMPLE_RATE - var layer string = "" // not set since type is always DEFAULT_SAMPLE_RATE - - ns := newOboeSettings() - ns.source = settingType(sType).toSampleSource() - ns.layer = layer - - ns.timestamp = time.Unix(int64(settingLambda.Timestamp), 0) - ns.flags = flagStringToBin(settingLambda.Flags) - ns.originalFlags = flagStringToBin(settingLambda.Flags) - ns.value = adjustSampleRate(int64(settingLambda.Value)) - ns.ttl = int64(settingLambda.Ttl) - - rate := float64(settingLambda.Arguments.BucketRate) - capacity := float64(settingLambda.Arguments.BucketCapacity) - ns.bucket.setRateCap(rate, capacity) - - tRelaxedRate := float64(settingLambda.Arguments.TriggerRelaxedBucketRate) - tRelaxedCapacity := float64(settingLambda.Arguments.TriggerRelaxedBucketCapacity) - ns.triggerTraceRelaxedBucket.setRateCap(tRelaxedRate, tRelaxedCapacity) - - tStrictRate := float64(settingLambda.Arguments.TriggerStrictBucketRate) - tStrictCapacity := float64(settingLambda.Arguments.TriggerStrictBucketCapacity) - ns.triggerTraceStrictBucket.setRateCap(tStrictRate, tStrictCapacity) - - merged := mergeLocalSetting(ns) - - key := settingKey{ - sType: settingType(sType), - layer: layer, - } - - o.Lock() - o.settings[key] = merged - o.Unlock() + o.UpdateSetting( + settingLambda.sType, + settingLambda.layer, + settingLambda.flags, + settingLambda.value, + settingLambda.ttl, + settingLambda.args, + ) } func (o *oboe) StartSettingTicker() { diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index b1e637a7..d711161e 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -15,14 +15,18 @@ package oboe import ( + "encoding/binary" "encoding/json" "errors" "io" stdlog "log" + "math" "os" + + "github.com/solarwinds/apm-go/internal/constants" ) -type settingLambda struct { +type settingLambdaFromFile struct { Arguments *settingArguments `json:"arguments"` Flags string `json:"flags"` Layer string `json:"layer"` @@ -35,15 +39,54 @@ type settingLambda struct { type settingArguments struct { BucketCapacity float64 `json:"BucketCapacity"` BucketRate float64 `json:"BucketRate"` - MetricsFlushInterval int64 `json:"MetricsFlushInterval"` + MetricsFlushInterval float64 `json:"MetricsFlushInterval"` TriggerRelaxedBucketCapacity float64 `json:"TriggerRelaxedBucketCapacity"` TriggerRelaxedBucketRate float64 `json:"TriggerRelaxedBucketRate"` TriggerStrictBucketCapacity float64 `json:"TriggerStrictBucketCapacity"` TriggerStrictBucketRate float64 `json:"TriggerStrictBucketRate"` } -func newSettingLambdaFromFile() (*settingLambda, error) { - // TODO tracing mode disabled if cannot open/parse/get all settings +type settingLambdaNormalized struct { + sType int32 + layer string + flags []byte + value int64 + ttl int64 + args map[string][]byte +} + +// TODO(?): consolidate with internal/oboetestutils/oboe.go argsToMap +func floatToBytes(float float64) []byte { + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, math.Float64bits(float)) + return bytes +} + +func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaNormalized { + flags := []byte(fromFile.Flags) + + args := make(map[string][]byte) + args[constants.KvBucketCapacity] = floatToBytes(fromFile.Arguments.BucketCapacity) + args[constants.KvBucketRate] = floatToBytes(fromFile.Arguments.BucketRate) + args[constants.KvMetricsFlushInterval] = floatToBytes(fromFile.Arguments.MetricsFlushInterval) + args[constants.KvTriggerTraceRelaxedBucketCapacity] = floatToBytes(fromFile.Arguments.TriggerRelaxedBucketCapacity) + args[constants.KvTriggerTraceRelaxedBucketRate] = floatToBytes(fromFile.Arguments.TriggerRelaxedBucketRate) + args[constants.KvTriggerTraceStrictBucketCapacity] = floatToBytes(fromFile.Arguments.TriggerStrictBucketCapacity) + args[constants.KvTriggerTraceStrictBucketRate] = floatToBytes(fromFile.Arguments.TriggerStrictBucketRate) + + settingNorm := settingLambdaNormalized{ + 1, // always DEFAULT_SAMPLE_RATE + "", // not set since type is always DEFAULT_SAMPLE_RATE + flags, + int64(fromFile.Value), + int64(fromFile.Ttl), + args, + } + + return &settingNorm +} + +func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") if err != nil { return nil, err @@ -53,7 +96,7 @@ func newSettingLambdaFromFile() (*settingLambda, error) { return nil, err } // Settings file should be an array with a single settings object - var settingLambdas []settingLambda + var settingLambdas []settingLambdaFromFile if err := json.Unmarshal(settingBytes, &settingLambdas); err != nil { return nil, err } @@ -61,10 +104,13 @@ func newSettingLambdaFromFile() (*settingLambda, error) { return nil, errors.New("settings file is incorrectly formatted") } - var settingLambda settingLambda = settingLambdas[0] + var settingLambda settingLambdaFromFile = settingLambdas[0] // tmp: debug stdlog.Printf("settingLambda: %v", settingLambda) stdlog.Printf("settingLambda.Arguments: %v", settingLambda.Arguments) - return &settingLambda, nil + var settingLambdaNormalized = newSettingLambdaNormalized(&settingLambda) + // tmp: debug + stdlog.Printf("settingLambdaNormalized: %v", settingLambdaNormalized) + return settingLambdaNormalized, nil } From 42d2b332d9d3a6eac9cd6fb4264f3878c245f39a Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 12:58:27 -0700 Subject: [PATCH 10/39] Revert "HasLambdaEnv more public" This reverts commit bfffc2f4c7e5102287e8d09a4aeb1ef9b1868f51. --- internal/config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 34a99cd9..c9ef73d0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -340,8 +340,8 @@ const ( may be different from your setting.` ) -// HasLambdaEnv checks if the AWS Lambda env var is set. -func HasLambdaEnv() bool { +// hasLambdaEnv checks if the AWS Lambda env var is set. +func hasLambdaEnv() bool { return os.Getenv("AWS_LAMBDA_FUNCTION_NAME") != "" && os.Getenv("LAMBDA_TASK_ROOT") != "" } @@ -362,12 +362,12 @@ func (c *Config) validate() error { c.Ec2MetadataTimeout = t } - if c.TransactionName != "" && !HasLambdaEnv() { + if c.TransactionName != "" && !hasLambdaEnv() { log.Info(InvalidEnv("TransactionName", c.TransactionName)) c.TransactionName = getFieldDefaultValue(c, "TransactionName") } - if !HasLambdaEnv() { + if !hasLambdaEnv() { if c.ServiceKey != "" { c.ServiceKey = ToServiceKey(c.ServiceKey) if ok := IsValidServiceKey(c.ServiceKey); !ok { From c041b6d87a9e971abd1e49390d590ea99e284d9d Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 14:03:54 -0700 Subject: [PATCH 11/39] Mv ArgsToMap to general utils --- internal/oboe/settings_lambda.go | 33 ++++++-------- internal/oboetestutils/oboe.go | 78 ++++---------------------------- internal/utils/utils.go | 59 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 88 deletions(-) diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index d711161e..93eca2d9 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -15,15 +15,13 @@ package oboe import ( - "encoding/binary" "encoding/json" "errors" "io" stdlog "log" - "math" "os" - "github.com/solarwinds/apm-go/internal/constants" + "github.com/solarwinds/apm-go/internal/utils" ) type settingLambdaFromFile struct { @@ -39,7 +37,7 @@ type settingLambdaFromFile struct { type settingArguments struct { BucketCapacity float64 `json:"BucketCapacity"` BucketRate float64 `json:"BucketRate"` - MetricsFlushInterval float64 `json:"MetricsFlushInterval"` + MetricsFlushInterval int `json:"MetricsFlushInterval"` TriggerRelaxedBucketCapacity float64 `json:"TriggerRelaxedBucketCapacity"` TriggerRelaxedBucketRate float64 `json:"TriggerRelaxedBucketRate"` TriggerStrictBucketCapacity float64 `json:"TriggerStrictBucketCapacity"` @@ -55,24 +53,21 @@ type settingLambdaNormalized struct { args map[string][]byte } -// TODO(?): consolidate with internal/oboetestutils/oboe.go argsToMap -func floatToBytes(float float64) []byte { - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, math.Float64bits(float)) - return bytes -} - func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaNormalized { flags := []byte(fromFile.Flags) - args := make(map[string][]byte) - args[constants.KvBucketCapacity] = floatToBytes(fromFile.Arguments.BucketCapacity) - args[constants.KvBucketRate] = floatToBytes(fromFile.Arguments.BucketRate) - args[constants.KvMetricsFlushInterval] = floatToBytes(fromFile.Arguments.MetricsFlushInterval) - args[constants.KvTriggerTraceRelaxedBucketCapacity] = floatToBytes(fromFile.Arguments.TriggerRelaxedBucketCapacity) - args[constants.KvTriggerTraceRelaxedBucketRate] = floatToBytes(fromFile.Arguments.TriggerRelaxedBucketRate) - args[constants.KvTriggerTraceStrictBucketCapacity] = floatToBytes(fromFile.Arguments.TriggerStrictBucketCapacity) - args[constants.KvTriggerTraceStrictBucketRate] = floatToBytes(fromFile.Arguments.TriggerStrictBucketRate) + var unusedToken = "TOKEN" + args := utils.ArgsToMap( + fromFile.Arguments.BucketCapacity, + fromFile.Arguments.BucketRate, + fromFile.Arguments.TriggerRelaxedBucketCapacity, + fromFile.Arguments.TriggerRelaxedBucketRate, + fromFile.Arguments.TriggerStrictBucketCapacity, + fromFile.Arguments.TriggerStrictBucketRate, + fromFile.Arguments.MetricsFlushInterval, + -1, + []byte(unusedToken), + ) settingNorm := settingLambdaNormalized{ 1, // always DEFAULT_SAMPLE_RATE diff --git a/internal/oboetestutils/oboe.go b/internal/oboetestutils/oboe.go index 3bd4a5cd..6fb293fe 100644 --- a/internal/oboetestutils/oboe.go +++ b/internal/oboetestutils/oboe.go @@ -14,71 +14,11 @@ package oboetestutils -import ( - "encoding/binary" - "github.com/solarwinds/apm-go/internal/constants" - "math" -) +import "github.com/solarwinds/apm-go/internal/utils" const TestToken = "TOKEN" const TypeDefault = 0 -func argsToMap(capacity, ratePerSec, tRCap, tRRate, tSCap, tSRate float64, - metricsFlushInterval, maxTransactions int, token []byte) map[string][]byte { - args := make(map[string][]byte) - - if capacity > -1 { - bits := math.Float64bits(capacity) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvBucketCapacity] = bytes - } - if ratePerSec > -1 { - bits := math.Float64bits(ratePerSec) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvBucketRate] = bytes - } - if tRCap > -1 { - bits := math.Float64bits(tRCap) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvTriggerTraceRelaxedBucketCapacity] = bytes - } - if tRRate > -1 { - bits := math.Float64bits(tRRate) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvTriggerTraceRelaxedBucketRate] = bytes - } - if tSCap > -1 { - bits := math.Float64bits(tSCap) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvTriggerTraceStrictBucketCapacity] = bytes - } - if tSRate > -1 { - bits := math.Float64bits(tSRate) - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, bits) - args[constants.KvTriggerTraceStrictBucketRate] = bytes - } - if metricsFlushInterval > -1 { - bytes := make([]byte, 4) - binary.LittleEndian.PutUint32(bytes, uint32(metricsFlushInterval)) - args[constants.KvMetricsFlushInterval] = bytes - } - if maxTransactions > -1 { - bytes := make([]byte, 4) - binary.LittleEndian.PutUint32(bytes, uint32(maxTransactions)) - args[constants.KvMaxTransactions] = bytes - } - - args[constants.KvSignatureKey] = token - - return args -} - type SettingUpdater interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) } @@ -87,48 +27,48 @@ func AddDefaultSetting(o SettingUpdater) { // add default setting with 100% sampling o.UpdateSetting(int32(TypeDefault), "", []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), - 1000000, 120, argsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddSampleThrough(o SettingUpdater) { // add default setting with 100% sampling o.UpdateSetting(int32(TypeDefault), "", []byte("SAMPLE_START,SAMPLE_THROUGH,TRIGGER_TRACE"), - 1000000, 120, argsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddNoTriggerTrace(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS"), - 1000000, 120, argsToMap(1000000, 1000000, 0, 0, 0, 0, -1, -1, []byte(TestToken))) + 1000000, 120, utils.ArgsToMap(1000000, 1000000, 0, 0, 0, 0, -1, -1, []byte(TestToken))) } func AddTriggerTraceOnly(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte("TRIGGER_TRACE"), - 0, 120, argsToMap(0, 0, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddRelaxedTriggerTraceOnly(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte("TRIGGER_TRACE"), - 0, 120, argsToMap(0, 0, 1000000, 1000000, 0, 0, -1, -1, []byte(TestToken))) + 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 0, 0, -1, -1, []byte(TestToken))) } func AddStrictTriggerTraceOnly(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte("TRIGGER_TRACE"), - 0, 120, argsToMap(0, 0, 0, 0, 1000000, 1000000, -1, -1, []byte(TestToken))) + 0, 120, utils.ArgsToMap(0, 0, 0, 0, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddLimitedTriggerTrace(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), - 1000000, 120, argsToMap(1000000, 1000000, 1, 1, 1, 1, -1, -1, []byte(TestToken))) + 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1, 1, 1, 1, -1, -1, []byte(TestToken))) } func AddDisabled(o SettingUpdater) { o.UpdateSetting(int32(TypeDefault), "", []byte(""), - 0, 120, argsToMap(0, 0, 1, 1, 1, 1, -1, -1, []byte(TestToken))) + 0, 120, utils.ArgsToMap(0, 0, 1, 1, 1, 1, -1, -1, []byte(TestToken))) } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 36e9c1f6..b547de98 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -17,12 +17,15 @@ package utils import ( "bufio" "bytes" + "encoding/binary" "encoding/json" + "math" "os" "runtime" "strings" "sync" + "github.com/solarwinds/apm-go/internal/constants" "gopkg.in/mgo.v2/bson" ) @@ -133,6 +136,62 @@ func IsHigherOrEqualGoVersion(version string) bool { return true } +func ArgsToMap(capacity, ratePerSec, tRCap, tRRate, tSCap, tSRate float64, + metricsFlushInterval, maxTransactions int, token []byte) map[string][]byte { + args := make(map[string][]byte) + + if capacity > -1 { + bits := math.Float64bits(capacity) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvBucketCapacity] = bytes + } + if ratePerSec > -1 { + bits := math.Float64bits(ratePerSec) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvBucketRate] = bytes + } + if tRCap > -1 { + bits := math.Float64bits(tRCap) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvTriggerTraceRelaxedBucketCapacity] = bytes + } + if tRRate > -1 { + bits := math.Float64bits(tRRate) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvTriggerTraceRelaxedBucketRate] = bytes + } + if tSCap > -1 { + bits := math.Float64bits(tSCap) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvTriggerTraceStrictBucketCapacity] = bytes + } + if tSRate > -1 { + bits := math.Float64bits(tSRate) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + args[constants.KvTriggerTraceStrictBucketRate] = bytes + } + if metricsFlushInterval > -1 { + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, uint32(metricsFlushInterval)) + args[constants.KvMetricsFlushInterval] = bytes + } + if maxTransactions > -1 { + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, uint32(maxTransactions)) + args[constants.KvMaxTransactions] = bytes + } + + args[constants.KvSignatureKey] = token + + return args +} + // SafeBuffer is goroutine-safe buffer. It is for internal test use only. type SafeBuffer struct { buf bytes.Buffer From cf63ef9ba887d5d67a3954a4466c6b2463b59a57 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 15:28:32 -0700 Subject: [PATCH 12/39] Add FileBasedWatcher --- internal/oboe/file_watcher.go | 69 +++++++++++++++++++++++++++++++++++ internal/oboe/oboe.go | 34 +---------------- swo/agent.go | 5 ++- 3 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 internal/oboe/file_watcher.go diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go new file mode 100644 index 00000000..16a25b30 --- /dev/null +++ b/internal/oboe/file_watcher.go @@ -0,0 +1,69 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 oboe + +import ( + stdlog "log" + "time" +) + +const ( + tick = 10 + tickUnit = time.Second +) + +type FileBasedWatcher interface { + UpdateSettingFromFile() + Start() + Stop() +} + +func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { + return &fileBasedWatcher{ + *oboe, + time.NewTicker(tick * tickUnit), + } +} + +type fileBasedWatcher struct { + o Oboe + ticker *time.Ticker +} + +func (fbw *fileBasedWatcher) UpdateSettingFromFile() { + settingLambda, err := newSettingLambdaFromFile() + if err != nil { + stdlog.Fatalf("Could not update setting from file: %s", err) + return + } + fbw.o.UpdateSetting( + settingLambda.sType, + settingLambda.layer, + settingLambda.flags, + settingLambda.value, + settingLambda.ttl, + settingLambda.args, + ) +} + +func (fbw *fileBasedWatcher) Start() { + for range fbw.ticker.C { + fbw.UpdateSettingFromFile() + } +} + +func (fbw *fileBasedWatcher) Stop() { + fbw.ticker.Stop() +} diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index f8d8e959..f4b2dbf2 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -18,7 +18,6 @@ import ( "encoding/binary" "errors" "fmt" - stdlog "log" "math" "strings" "sync" @@ -50,9 +49,6 @@ const ( type Oboe interface { UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) - UpdateSettingFromFile() - StartSettingTicker() - StopSettingTicker() CheckSettingsTimeout() GetSetting() (*settings, bool) RemoveSetting() @@ -70,8 +66,7 @@ func NewOboe() Oboe { type oboe struct { sync.RWMutex - settings map[settingKey]*settings - settingsTicker *time.Ticker + settings map[settingKey]*settings } var _ Oboe = &oboe{} @@ -256,33 +251,6 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 o.Unlock() } -func (o *oboe) UpdateSettingFromFile() { - settingLambda, err := newSettingLambdaFromFile() - if err != nil { - stdlog.Fatalf("Could not update setting from file: %s", err) - return - } - o.UpdateSetting( - settingLambda.sType, - settingLambda.layer, - settingLambda.flags, - settingLambda.value, - settingLambda.ttl, - settingLambda.args, - ) -} - -func (o *oboe) StartSettingTicker() { - o.settingsTicker = time.NewTicker(10 * time.Second) - for range o.settingsTicker.C { - o.UpdateSettingFromFile() - } -} - -func (o *oboe) StopSettingTicker() { - o.settingsTicker.Stop() -} - // CheckSettingsTimeout checks and deletes expired settings func (o *oboe) CheckSettingsTimeout() { o.checkSettingsTimeout() diff --git a/swo/agent.go b/swo/agent.go index af49964a..7e278eb7 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -94,7 +94,8 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { o := oboe.NewOboe() // TODO only if in lambda - go o.StartSettingTicker() + fbw := oboe.NewFileBasedWatcher(&o) + go fbw.Start() _reporter, err := reporter.Start(resrc, registry, o) if err != nil { @@ -124,7 +125,7 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { return func() { // stop ticker // TODO only if in lambda - o.StopSettingTicker() + fbw.Stop() // shut down TracerProvider, and log error if issues if err := tp.Shutdown(context.Background()); err != nil { From 4164796f475df41fb49d8a252a85ae76ebfdf161 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 15:46:18 -0700 Subject: [PATCH 13/39] Fix Watcher ticker start/stop --- internal/oboe/file_watcher.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 16a25b30..eda3abc1 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -24,6 +24,8 @@ const ( tickUnit = time.Second ) +var exit = make(chan bool, 1) + type FileBasedWatcher interface { UpdateSettingFromFile() Start() @@ -33,13 +35,11 @@ type FileBasedWatcher interface { func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { return &fileBasedWatcher{ *oboe, - time.NewTicker(tick * tickUnit), } } type fileBasedWatcher struct { - o Oboe - ticker *time.Ticker + o Oboe } func (fbw *fileBasedWatcher) UpdateSettingFromFile() { @@ -59,11 +59,22 @@ func (fbw *fileBasedWatcher) UpdateSettingFromFile() { } func (fbw *fileBasedWatcher) Start() { - for range fbw.ticker.C { - fbw.UpdateSettingFromFile() - } + ticker := time.NewTicker(tick * tickUnit) + go func() { + defer ticker.Stop() + for { + select { + case <-exit: + return + case <-ticker.C: + stdlog.Print("Updating settings from file.") + fbw.UpdateSettingFromFile() + } + } + }() } func (fbw *fileBasedWatcher) Stop() { - fbw.ticker.Stop() + stdlog.Print("Stopping settings file watcher.") + exit <- true } From 8dccd788f505e40c9a2eaa4e6d82ab06cb01a125 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 15:50:34 -0700 Subject: [PATCH 14/39] Rm debug lines --- internal/oboe/settings_lambda.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 93eca2d9..baa50091 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -18,7 +18,6 @@ import ( "encoding/json" "errors" "io" - stdlog "log" "os" "github.com/solarwinds/apm-go/internal/utils" @@ -100,12 +99,7 @@ func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { } var settingLambda settingLambdaFromFile = settingLambdas[0] - // tmp: debug - stdlog.Printf("settingLambda: %v", settingLambda) - stdlog.Printf("settingLambda.Arguments: %v", settingLambda.Arguments) var settingLambdaNormalized = newSettingLambdaNormalized(&settingLambda) - // tmp: debug - stdlog.Printf("settingLambdaNormalized: %v", settingLambdaNormalized) return settingLambdaNormalized, nil } From 8efdc1bb7dbafd22dd19d1a4d2127164d909f996 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 15:50:56 -0700 Subject: [PATCH 15/39] Rm Fbw init for now --- swo/agent.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/swo/agent.go b/swo/agent.go index 7e278eb7..f5846d5e 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -89,24 +89,18 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { // return a no-op func so that we don't cause a nil-deref for the end-user }, err } - config.Load() // earlier so we can call HasLambdaEnv earlier registry := metrics.NewLegacyRegistry() o := oboe.NewOboe() - - // TODO only if in lambda - fbw := oboe.NewFileBasedWatcher(&o) - go fbw.Start() - _reporter, err := reporter.Start(resrc, registry, o) if err != nil { return func() {}, err } - exprtr := exporter.NewExporter(_reporter) smplr, err := sampler.NewSampler(o) if err != nil { return func() {}, err } + config.Load() isAppoptics := strings.Contains(strings.ToLower(config.GetCollector()), "appoptics.com") proc := processor.NewInboundMetricsSpanProcessor(registry, isAppoptics) prop := propagation.NewCompositeTextMapPropagator( @@ -123,11 +117,6 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { ) otel.SetTracerProvider(tp) return func() { - // stop ticker - // TODO only if in lambda - fbw.Stop() - - // shut down TracerProvider, and log error if issues if err := tp.Shutdown(context.Background()); err != nil { stdlog.Fatal(err) } From 2c64608189986c4372be2ce24a88be4f8db89d7f Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 16:38:58 -0700 Subject: [PATCH 16/39] Add tests --- internal/oboe/settings_lambda_test.go | 99 ++++++++++++++++++++ internal/utils/utils_test.go | 124 ++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 internal/oboe/settings_lambda_test.go create mode 100644 internal/utils/utils_test.go diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go new file mode 100644 index 00000000..5be9bf30 --- /dev/null +++ b/internal/oboe/settings_lambda_test.go @@ -0,0 +1,99 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 oboe + +import ( + "testing" + + "github.com/solarwinds/apm-go/internal/constants" + "github.com/stretchr/testify/assert" +) + +func TestNewSettingLambdaNormalized(t *testing.T) { + settingArgs := settingArguments{ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + } + fromFile := settingLambdaFromFile{ + &settingArgs, + "SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE", + "", + 1715900164, + 120, + 0, + 1000000, + } + result := newSettingLambdaNormalized(&fromFile) + + assert.Equal(t, int32(1), result.sType) + assert.Equal(t, "", result.layer) + assert.Equal( + t, + []byte{0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x55, 0x47, 0x48, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x2c, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45}, + result.flags, + ) + assert.Equal(t, result.value, int64(1000000)) + assert.Equal(t, result.ttl, int64(120)) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceRelaxedBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceRelaxedBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceStrictBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceStrictBucketRate], + ) + assert.Equal( + t, + []byte{0x1, 0x0, 0x0, 0x0}, + result.args[constants.KvMetricsFlushInterval], + ) + assert.Equal( + t, + []byte(nil), + result.args[constants.KvMaxTransactions], + ) + assert.Equal( + t, + []byte{0x54, 0x4f, 0x4b, 0x45, 0x4e}, + result.args[constants.KvSignatureKey], + ) +} diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go new file mode 100644 index 00000000..0b48f129 --- /dev/null +++ b/internal/utils/utils_test.go @@ -0,0 +1,124 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 utils + +import ( + "testing" + + "github.com/solarwinds/apm-go/internal/constants" + "github.com/stretchr/testify/assert" +) + +const TestToken = "TOKEN" + +func TestArgsToMapAllSet(t *testing.T) { + result := ArgsToMap(1, 1, 1, 1, 1, 1, 1, 1, []byte(TestToken)) + + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvTriggerTraceRelaxedBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvTriggerTraceRelaxedBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvTriggerTraceStrictBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result[constants.KvTriggerTraceStrictBucketRate], + ) + assert.Equal( + t, + []byte{0x1, 0x0, 0x0, 0x0}, + result[constants.KvMetricsFlushInterval], + ) + assert.Equal( + t, + []byte{0x1, 0x0, 0x0, 0x0}, + result[constants.KvMaxTransactions], + ) + assert.Equal( + t, + []byte{0x54, 0x4f, 0x4b, 0x45, 0x4e}, + result[constants.KvSignatureKey], + ) +} + +func TestArgsToMapAllUnset(t *testing.T) { + result := ArgsToMap(-1, -1, -1, -1, -1, -1, -1, -1, []byte(TestToken)) + + assert.Equal( + t, + []byte(nil), + result[constants.KvBucketCapacity], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvBucketRate], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvTriggerTraceRelaxedBucketCapacity], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvTriggerTraceRelaxedBucketRate], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvTriggerTraceStrictBucketCapacity], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvTriggerTraceStrictBucketRate], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvMetricsFlushInterval], + ) + assert.Equal( + t, + []byte(nil), + result[constants.KvMaxTransactions], + ) + assert.Equal( + t, + []byte{0x54, 0x4f, 0x4b, 0x45, 0x4e}, + result[constants.KvSignatureKey], + ) +} From ea018e94e1466ec8d450fd86a4e8902beda75b77 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 17:04:04 -0700 Subject: [PATCH 17/39] More tests --- internal/oboe/settings_lambda_test.go | 100 ++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go index 5be9bf30..ec6a9601 100644 --- a/internal/oboe/settings_lambda_test.go +++ b/internal/oboe/settings_lambda_test.go @@ -15,12 +15,16 @@ package oboe import ( + "os" "testing" "github.com/solarwinds/apm-go/internal/constants" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +const SettingsFile = "/tmp/solarwinds-apm-settings.json" + func TestNewSettingLambdaNormalized(t *testing.T) { settingArgs := settingArguments{ 1, @@ -97,3 +101,99 @@ func TestNewSettingLambdaNormalized(t *testing.T) { result.args[constants.KvSignatureKey], ) } + +func TestNewSettingLambdaFromFileErrorOpen(t *testing.T) { + require.NoFileExists(t, SettingsFile) + res, err := newSettingLambdaFromFile() + assert.Nil(t, res) + assert.Error(t, err) +} + +func TestNewSettingLambdaFromFileErrorUnmarshal(t *testing.T) { + require.NoFileExists(t, SettingsFile) + + content := []byte("hello\ngo\n") + os.WriteFile(SettingsFile, content, 0644) + res, err := newSettingLambdaFromFile() + assert.Nil(t, res) + assert.Error(t, err) + + os.Remove(SettingsFile) +} + +func TestNewSettingLambdaFromFileErrorLen(t *testing.T) { + require.NoFileExists(t, SettingsFile) + + content := []byte("[]") + os.WriteFile(SettingsFile, content, 0644) + res, err := newSettingLambdaFromFile() + assert.Nil(t, res) + assert.Error(t, err) + + os.Remove(SettingsFile) +} + +func TestNewSettingLambdaFromFile(t *testing.T) { + require.NoFileExists(t, SettingsFile) + + content := []byte("[{\"arguments\":{\"BucketCapacity\":1,\"BucketRate\":1,\"MetricsFlushInterval\":1,\"TriggerRelaxedBucketCapacity\":1,\"TriggerRelaxedBucketRate\":1,\"TriggerStrictBucketCapacity\":1,\"TriggerStrictBucketRate\":1},\"flags\":\"SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE\",\"layer\":\"\",\"timestamp\":1715900164,\"ttl\":120,\"type\":0,\"value\":1000000}]") + os.WriteFile(SettingsFile, content, 0644) + result, err := newSettingLambdaFromFile() + assert.Nil(t, err) + assert.Equal(t, int32(1), result.sType) + assert.Equal(t, "", result.layer) + assert.Equal( + t, + []byte{0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x55, 0x47, 0x48, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x2c, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45}, + result.flags, + ) + assert.Equal(t, result.value, int64(1000000)) + assert.Equal(t, result.ttl, int64(120)) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceRelaxedBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceRelaxedBucketRate], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceStrictBucketCapacity], + ) + assert.Equal( + t, + []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + result.args[constants.KvTriggerTraceStrictBucketRate], + ) + assert.Equal( + t, + []byte{0x1, 0x0, 0x0, 0x0}, + result.args[constants.KvMetricsFlushInterval], + ) + assert.Equal( + t, + []byte(nil), + result.args[constants.KvMaxTransactions], + ) + assert.Equal( + t, + []byte{0x54, 0x4f, 0x4b, 0x45, 0x4e}, + result.args[constants.KvSignatureKey], + ) + + os.Remove(SettingsFile) +} From 15f719cdcc4644b120ae6b5dc5bcd2f3fdec6b94 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 17:13:26 -0700 Subject: [PATCH 18/39] Add comments --- internal/oboe/file_watcher.go | 2 ++ internal/oboe/settings_lambda.go | 4 ++++ internal/utils/utils.go | 2 ++ 3 files changed, 8 insertions(+) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index eda3abc1..135247ea 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -32,6 +32,8 @@ type FileBasedWatcher interface { Stop() } +// NewFileBasedWatcher returns a FileBasedWatcher that periodically +// does oboe.UpdateSetting using values from a settings JSON file. func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { return &fileBasedWatcher{ *oboe, diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index baa50091..4c137ae7 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -52,6 +52,8 @@ type settingLambdaNormalized struct { args map[string][]byte } +// newSettingLambdaNormalized uses settings in json-unmarshalled format +// for mapping to a format readable by oboe UpdateSetting. func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaNormalized { flags := []byte(fromFile.Flags) @@ -80,6 +82,8 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN return &settingNorm } +// newSettingLambdaFromFile unmarshals sampling settings from a JSON file at a +// specific path in a specific format, else returns error. func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") if err != nil { diff --git a/internal/utils/utils.go b/internal/utils/utils.go index b547de98..5f73755c 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -136,6 +136,8 @@ func IsHigherOrEqualGoVersion(version string) bool { return true } +// ArgsToMap uses settings as float/int/bytes to create a map of string keys +// to bytes, for usability by oboe UpdateSetting. func ArgsToMap(capacity, ratePerSec, tRCap, tRRate, tSCap, tSRate float64, metricsFlushInterval, maxTransactions int, token []byte) map[string][]byte { args := make(map[string][]byte) From 189c022dac59cae3263f09e2210eb147fc65c32f Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Tue, 21 May 2024 17:16:08 -0700 Subject: [PATCH 19/39] Add errchecks --- internal/oboe/settings_lambda_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go index ec6a9601..67e075ad 100644 --- a/internal/oboe/settings_lambda_test.go +++ b/internal/oboe/settings_lambda_test.go @@ -113,7 +113,7 @@ func TestNewSettingLambdaFromFileErrorUnmarshal(t *testing.T) { require.NoFileExists(t, SettingsFile) content := []byte("hello\ngo\n") - os.WriteFile(SettingsFile, content, 0644) + require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) res, err := newSettingLambdaFromFile() assert.Nil(t, res) assert.Error(t, err) @@ -125,7 +125,7 @@ func TestNewSettingLambdaFromFileErrorLen(t *testing.T) { require.NoFileExists(t, SettingsFile) content := []byte("[]") - os.WriteFile(SettingsFile, content, 0644) + require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) res, err := newSettingLambdaFromFile() assert.Nil(t, res) assert.Error(t, err) @@ -137,7 +137,7 @@ func TestNewSettingLambdaFromFile(t *testing.T) { require.NoFileExists(t, SettingsFile) content := []byte("[{\"arguments\":{\"BucketCapacity\":1,\"BucketRate\":1,\"MetricsFlushInterval\":1,\"TriggerRelaxedBucketCapacity\":1,\"TriggerRelaxedBucketRate\":1,\"TriggerStrictBucketCapacity\":1,\"TriggerStrictBucketRate\":1},\"flags\":\"SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE\",\"layer\":\"\",\"timestamp\":1715900164,\"ttl\":120,\"type\":0,\"value\":1000000}]") - os.WriteFile(SettingsFile, content, 0644) + require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) result, err := newSettingLambdaFromFile() assert.Nil(t, err) assert.Equal(t, int32(1), result.sType) From 6cb38cd7f2ceecd98a24c582f0eceb5f707f9ae6 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Wed, 22 May 2024 09:39:51 -0700 Subject: [PATCH 20/39] Update field int types --- internal/oboe/settings_lambda.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 4c137ae7..974bf6c8 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -27,10 +27,10 @@ type settingLambdaFromFile struct { Arguments *settingArguments `json:"arguments"` Flags string `json:"flags"` Layer string `json:"layer"` - Timestamp int `json:"timestamp"` + Timestamp int64 `json:"timestamp"` Ttl int64 `json:"ttl"` Stype int `json:"type"` - Value int `json:"value"` + Value int64 `json:"value"` } type settingArguments struct { From 79eb573feeb251416c0b215138b67607f7b5439f Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 23 May 2024 15:12:18 -0700 Subject: [PATCH 21/39] Add FileBasedWatcher settingsCache --- internal/oboe/file_watcher.go | 66 ++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 135247ea..37508b0b 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -15,53 +15,74 @@ package oboe import ( + "encoding/binary" stdlog "log" "time" + + "github.com/coocood/freecache" ) const ( - tick = 10 - tickUnit = time.Second + cacheSize = 5 * 1024 * 1024 // 5 MB + settingsCheckSeconds = 10 ) +var keyTtl []byte = []byte("ttl") var exit = make(chan bool, 1) type FileBasedWatcher interface { - UpdateSettingFromFile() Start() Stop() } // NewFileBasedWatcher returns a FileBasedWatcher that periodically -// does oboe.UpdateSetting using values from a settings JSON file. +// does oboe.UpdateSetting using values from a settings JSON file, +// if cached settings have not yet expired. func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { return &fileBasedWatcher{ *oboe, + *freecache.NewCache(cacheSize), } } type fileBasedWatcher struct { - o Oboe + o Oboe + settingsCache freecache.Cache +} + +// updateCacheFromFile sets "ttl" as byte representation of ttl in seconds. +func (fbw *fileBasedWatcher) updateCacheFromFile(sl *settingLambdaNormalized) { + ttlBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(ttlBytes, uint64(sl.ttl)) + fbw.settingsCache.Set(keyTtl, ttlBytes, int(sl.ttl)) } -func (fbw *fileBasedWatcher) UpdateSettingFromFile() { +// updateSettingFromFile updates oboe settings using normalized settings from file. +func (fbw *fileBasedWatcher) updateSettingFromFile(sl *settingLambdaNormalized) { + fbw.o.UpdateSetting( + sl.sType, + sl.layer, + sl.flags, + sl.value, + sl.ttl, + sl.args, + ) +} + +func (fbw *fileBasedWatcher) updateSettingAndCacheFromFile() { settingLambda, err := newSettingLambdaFromFile() if err != nil { stdlog.Fatalf("Could not update setting from file: %s", err) return } - fbw.o.UpdateSetting( - settingLambda.sType, - settingLambda.layer, - settingLambda.flags, - settingLambda.value, - settingLambda.ttl, - settingLambda.args, - ) + fbw.updateSettingFromFile(settingLambda) + fbw.updateCacheFromFile(settingLambda) } +// Start runs a ticker that checks settings expiry from cache +// and, if expired, updates cache and oboe settings. func (fbw *fileBasedWatcher) Start() { - ticker := time.NewTicker(tick * tickUnit) + ticker := time.NewTicker(settingsCheckSeconds * time.Second) go func() { defer ticker.Stop() for { @@ -69,8 +90,19 @@ func (fbw *fileBasedWatcher) Start() { case <-exit: return case <-ticker.C: - stdlog.Print("Updating settings from file.") - fbw.UpdateSettingFromFile() + _, expireAt, err := fbw.settingsCache.GetWithExpiration(keyTtl) + if err != nil { + stdlog.Fatalf("There was an issue with settingsCache: %s", err) + // TODO: disable APM Go + } else { + // If cached settings expired, update cache and + // and update oboe settings + remainingTime := expireAt - uint32(time.Now().Unix()) + if remainingTime <= 0 { + stdlog.Print("Updating settings from file.") + fbw.updateSettingAndCacheFromFile() + } + } } } }() From f0fe14abcef83eadbf7eb669daa27e905c18838c Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 23 May 2024 15:22:35 -0700 Subject: [PATCH 22/39] Add errcheck --- internal/oboe/file_watcher.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 37508b0b..dd49f60a 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -54,7 +54,11 @@ type fileBasedWatcher struct { func (fbw *fileBasedWatcher) updateCacheFromFile(sl *settingLambdaNormalized) { ttlBytes := make([]byte, 8) binary.LittleEndian.PutUint64(ttlBytes, uint64(sl.ttl)) - fbw.settingsCache.Set(keyTtl, ttlBytes, int(sl.ttl)) + err := fbw.settingsCache.Set(keyTtl, ttlBytes, int(sl.ttl)) + if err != nil { + stdlog.Fatalf("There was an issue with setting settingsCache: %s", err) + // TODO: disable APM Go + } } // updateSettingFromFile updates oboe settings using normalized settings from file. @@ -92,7 +96,7 @@ func (fbw *fileBasedWatcher) Start() { case <-ticker.C: _, expireAt, err := fbw.settingsCache.GetWithExpiration(keyTtl) if err != nil { - stdlog.Fatalf("There was an issue with settingsCache: %s", err) + stdlog.Fatalf("There was an issue with getting settingsCache: %s", err) // TODO: disable APM Go } else { // If cached settings expired, update cache and From c2e4599f0892ef99a6a8f66ab2f28484506b9ace Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 23 May 2024 16:11:17 -0700 Subject: [PATCH 23/39] Fix FileWatcher cache get/set --- internal/oboe/file_watcher.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index dd49f60a..c5f3c1a0 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -17,6 +17,7 @@ package oboe import ( "encoding/binary" stdlog "log" + "math" "time" "github.com/coocood/freecache" @@ -52,8 +53,9 @@ type fileBasedWatcher struct { // updateCacheFromFile sets "ttl" as byte representation of ttl in seconds. func (fbw *fileBasedWatcher) updateCacheFromFile(sl *settingLambdaNormalized) { + ttlBits := math.Float64bits(float64(sl.ttl)) ttlBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(ttlBytes, uint64(sl.ttl)) + binary.LittleEndian.PutUint64(ttlBytes, ttlBits) err := fbw.settingsCache.Set(keyTtl, ttlBytes, int(sl.ttl)) if err != nil { stdlog.Fatalf("There was an issue with setting settingsCache: %s", err) @@ -94,18 +96,11 @@ func (fbw *fileBasedWatcher) Start() { case <-exit: return case <-ticker.C: - _, expireAt, err := fbw.settingsCache.GetWithExpiration(keyTtl) - if err != nil { - stdlog.Fatalf("There was an issue with getting settingsCache: %s", err) - // TODO: disable APM Go - } else { - // If cached settings expired, update cache and - // and update oboe settings - remainingTime := expireAt - uint32(time.Now().Unix()) - if remainingTime <= 0 { - stdlog.Print("Updating settings from file.") - fbw.updateSettingAndCacheFromFile() - } + // expired values are Not Found + _, notFound := fbw.settingsCache.Get(keyTtl) + if notFound != nil { + stdlog.Printf("Updating settings and cache from file.") + fbw.updateSettingAndCacheFromFile() } } } From 837d579122bbbf71d9f3175d9dd97c6a82b1ccff Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 23 May 2024 16:42:04 -0700 Subject: [PATCH 24/39] Comment out FileWatcher UpdateSetting call --- internal/oboe/file_watcher.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index c5f3c1a0..14bd818e 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -65,14 +65,15 @@ func (fbw *fileBasedWatcher) updateCacheFromFile(sl *settingLambdaNormalized) { // updateSettingFromFile updates oboe settings using normalized settings from file. func (fbw *fileBasedWatcher) updateSettingFromFile(sl *settingLambdaNormalized) { - fbw.o.UpdateSetting( - sl.sType, - sl.layer, - sl.flags, - sl.value, - sl.ttl, - sl.args, - ) + stdlog.Printf("TODO: Implement updateSettingFromFile. Received normalized settings %v", sl) + // fbw.o.UpdateSetting( + // sl.sType, + // sl.layer, + // sl.flags, + // sl.value, + // sl.ttl, + // sl.args, + // ) } func (fbw *fileBasedWatcher) updateSettingAndCacheFromFile() { @@ -99,7 +100,7 @@ func (fbw *fileBasedWatcher) Start() { // expired values are Not Found _, notFound := fbw.settingsCache.Get(keyTtl) if notFound != nil { - stdlog.Printf("Updating settings and cache from file.") + stdlog.Printf("Checking settings from file.") fbw.updateSettingAndCacheFromFile() } } From a2e93a272e3fe7c0b2bb386cb06c00366078b246 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 24 May 2024 10:15:17 -0700 Subject: [PATCH 25/39] Watcher cachesize 10kb --- internal/oboe/file_watcher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 14bd818e..9709d911 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -24,7 +24,7 @@ import ( ) const ( - cacheSize = 5 * 1024 * 1024 // 5 MB + cacheSize = 100 * 1024 // 100 kB settingsCheckSeconds = 10 ) From 69adc5e3dd6cdfad75149956e785db073d8650e6 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 24 May 2024 10:16:18 -0700 Subject: [PATCH 26/39] Fix pointer --- internal/oboe/settings_lambda.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 974bf6c8..dffd901c 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -70,7 +70,7 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN []byte(unusedToken), ) - settingNorm := settingLambdaNormalized{ + settingNorm := &settingLambdaNormalized{ 1, // always DEFAULT_SAMPLE_RATE "", // not set since type is always DEFAULT_SAMPLE_RATE flags, @@ -79,7 +79,7 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN args, } - return &settingNorm + return settingNorm } // newSettingLambdaFromFile unmarshals sampling settings from a JSON file at a From c8d5d25ba4eb05ee39a7d6dec6eaa889594ed7b5 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Fri, 24 May 2024 12:02:50 -0700 Subject: [PATCH 27/39] FileBasedWatcher does not cache nor updatesetting --- internal/oboe/file_watcher.go | 56 ++++++-------------------------- internal/oboe/settings_lambda.go | 5 +-- 2 files changed, 13 insertions(+), 48 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 9709d911..bb89c6b5 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -15,20 +15,14 @@ package oboe import ( - "encoding/binary" stdlog "log" - "math" "time" - - "github.com/coocood/freecache" ) const ( - cacheSize = 100 * 1024 // 100 kB settingsCheckSeconds = 10 ) -var keyTtl []byte = []byte("ttl") var exit = make(chan bool, 1) type FileBasedWatcher interface { @@ -37,53 +31,28 @@ type FileBasedWatcher interface { } // NewFileBasedWatcher returns a FileBasedWatcher that periodically -// does oboe.UpdateSetting using values from a settings JSON file, -// if cached settings have not yet expired. +// reads lambda settings from file func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { return &fileBasedWatcher{ *oboe, - *freecache.NewCache(cacheSize), } } type fileBasedWatcher struct { - o Oboe - settingsCache freecache.Cache -} - -// updateCacheFromFile sets "ttl" as byte representation of ttl in seconds. -func (fbw *fileBasedWatcher) updateCacheFromFile(sl *settingLambdaNormalized) { - ttlBits := math.Float64bits(float64(sl.ttl)) - ttlBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(ttlBytes, ttlBits) - err := fbw.settingsCache.Set(keyTtl, ttlBytes, int(sl.ttl)) - if err != nil { - stdlog.Fatalf("There was an issue with setting settingsCache: %s", err) - // TODO: disable APM Go - } -} - -// updateSettingFromFile updates oboe settings using normalized settings from file. -func (fbw *fileBasedWatcher) updateSettingFromFile(sl *settingLambdaNormalized) { - stdlog.Printf("TODO: Implement updateSettingFromFile. Received normalized settings %v", sl) - // fbw.o.UpdateSetting( - // sl.sType, - // sl.layer, - // sl.flags, - // sl.value, - // sl.ttl, - // sl.args, - // ) + o Oboe } -func (fbw *fileBasedWatcher) updateSettingAndCacheFromFile() { +// readSettingFromFile parses, normalizes, and print settings from file +func (fbw *fileBasedWatcher) readSettingFromFile() { settingLambda, err := newSettingLambdaFromFile() if err != nil { - stdlog.Fatalf("Could not update setting from file: %s", err) + stdlog.Fatalf("Could not read setting from file: %s", err) return } - fbw.updateSettingFromFile(settingLambda) - fbw.updateCacheFromFile(settingLambda) + stdlog.Printf( + "Got lambda settings from file:\n%v", + settingLambda, + ) } // Start runs a ticker that checks settings expiry from cache @@ -97,12 +66,7 @@ func (fbw *fileBasedWatcher) Start() { case <-exit: return case <-ticker.C: - // expired values are Not Found - _, notFound := fbw.settingsCache.Get(keyTtl) - if notFound != nil { - stdlog.Printf("Checking settings from file.") - fbw.updateSettingAndCacheFromFile() - } + fbw.readSettingFromFile() } } }() diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index dffd901c..7a4b6736 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -52,7 +52,7 @@ type settingLambdaNormalized struct { args map[string][]byte } -// newSettingLambdaNormalized uses settings in json-unmarshalled format +// newSettingLambdaNormalized accepts settings in json-unmarshalled format // for mapping to a format readable by oboe UpdateSetting. func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaNormalized { flags := []byte(fromFile.Flags) @@ -83,7 +83,8 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN } // newSettingLambdaFromFile unmarshals sampling settings from a JSON file at a -// specific path in a specific format, else returns error. +// specific path in a specific format then returns values normalized for +// oboe UpdateSetting, else returns error. func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") if err != nil { From eb061c29766e879bcd02cd2941817e377903f579 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Wed, 29 May 2024 11:44:25 -0700 Subject: [PATCH 28/39] Fix logging, style --- internal/oboe/file_watcher.go | 9 +++++---- internal/oboe/settings_lambda.go | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index bb89c6b5..add94858 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -15,8 +15,9 @@ package oboe import ( - stdlog "log" "time" + + "github.com/solarwinds/apm-go/internal/log" ) const ( @@ -46,10 +47,10 @@ type fileBasedWatcher struct { func (fbw *fileBasedWatcher) readSettingFromFile() { settingLambda, err := newSettingLambdaFromFile() if err != nil { - stdlog.Fatalf("Could not read setting from file: %s", err) + log.Errorf("Could not read setting from file: %s", err) return } - stdlog.Printf( + log.Debugf( "Got lambda settings from file:\n%v", settingLambda, ) @@ -73,6 +74,6 @@ func (fbw *fileBasedWatcher) Start() { } func (fbw *fileBasedWatcher) Stop() { - stdlog.Print("Stopping settings file watcher.") + log.Info("Stopping settings file watcher.") exit <- true } diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 7a4b6736..b574ed8a 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -103,8 +103,7 @@ func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { return nil, errors.New("settings file is incorrectly formatted") } - var settingLambda settingLambdaFromFile = settingLambdas[0] + settingLambda := settingLambdas[0] - var settingLambdaNormalized = newSettingLambdaNormalized(&settingLambda) - return settingLambdaNormalized, nil + return newSettingLambdaNormalized(&settingLambda), nil } From 32928235348d307daedba20866e54f1acd2305a4 Mon Sep 17 00:00:00 2001 From: Jared Harper <129781402+swi-jared@users.noreply.github.com> Date: Wed, 19 Jun 2024 08:17:02 -0700 Subject: [PATCH 29/39] [NH-78291] Lambda support (#98) --- examples/grpc/go.mod | 8 +- examples/grpc/go.sum | 4 + examples/http/go.mod | 8 +- examples/http/go.sum | 4 + go.mod | 32 +++-- go.sum | 75 ++++++++---- .../aws/aws-lambda-go/swolambda/README.md | 51 ++++++++ .../aws/aws-lambda-go/swolambda/go.mod | 49 ++++++++ .../aws/aws-lambda-go/swolambda/go.sum | 66 +++++++++++ .../aws/aws-lambda-go/swolambda/swolambda.go | 101 ++++++++++++++++ internal/config/config.go | 11 +- internal/entryspans/entryspans.go | 60 ++++++++-- internal/entryspans/entryspans_test.go | 18 ++- internal/exporter/exporter.go | 4 +- internal/metrics/metrics_test.go | 32 ++--- internal/metrics/otel.go | 77 ++++++++++++ internal/metrics/registry.go | 16 +-- internal/oboe/file_watcher.go | 84 +++++++++++-- internal/oboe/settings_lambda.go | 18 +-- internal/oboe/settings_lambda_test.go | 26 ++-- internal/processor/processor.go | 10 +- internal/processor/processor_test.go | 18 ++- internal/reporter/reporter_grpc.go | 2 +- internal/reporter/reporter_test.go | 10 +- internal/{utils/otel.go => txn/txn.go} | 51 ++++---- .../{utils/otel_test.go => txn/txn_test.go} | 43 ++++++- swo/agent.go | 112 ++++++++++++++++-- 27 files changed, 810 insertions(+), 180 deletions(-) create mode 100644 instrumentation/github.com/aws/aws-lambda-go/swolambda/README.md create mode 100644 instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod create mode 100644 instrumentation/github.com/aws/aws-lambda-go/swolambda/go.sum create mode 100644 instrumentation/github.com/aws/aws-lambda-go/swolambda/swolambda.go create mode 100644 internal/metrics/otel.go rename internal/{utils/otel.go => txn/txn.go} (65%) rename internal/{utils/otel_test.go => txn/txn_test.go} (66%) diff --git a/examples/grpc/go.mod b/examples/grpc/go.mod index c2500c9a..44771d06 100644 --- a/examples/grpc/go.mod +++ b/examples/grpc/go.mod @@ -22,7 +22,7 @@ replace github.com/solarwinds/apm-go => ../.. require ( github.com/solarwinds/apm-go v0.1.1 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 - go.opentelemetry.io/otel v1.25.0 + go.opentelemetry.io/otel v1.26.0 google.golang.org/grpc v1.63.2 google.golang.org/protobuf v1.33.0 ) @@ -38,9 +38,9 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - go.opentelemetry.io/otel/sdk v1.25.0 // indirect - go.opentelemetry.io/otel/trace v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect + go.opentelemetry.io/otel/sdk v1.26.0 // indirect + go.opentelemetry.io/otel/trace v1.26.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/examples/grpc/go.sum b/examples/grpc/go.sum index 095ef44e..051ab6ac 100644 --- a/examples/grpc/go.sum +++ b/examples/grpc/go.sum @@ -44,12 +44,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= diff --git a/examples/http/go.mod b/examples/http/go.mod index bc0a007a..e01a28b3 100644 --- a/examples/http/go.mod +++ b/examples/http/go.mod @@ -24,8 +24,8 @@ require ( github.com/mattn/go-sqlite3 v1.14.18 github.com/solarwinds/apm-go v0.1.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 - go.opentelemetry.io/otel v1.25.0 - go.opentelemetry.io/otel/trace v1.25.0 + go.opentelemetry.io/otel v1.26.0 + go.opentelemetry.io/otel/trace v1.26.0 ) require ( @@ -40,8 +40,8 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect + go.opentelemetry.io/otel/sdk v1.26.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.19.0 // indirect diff --git a/examples/http/go.sum b/examples/http/go.sum index 157b4c1a..f0608677 100644 --- a/examples/http/go.sum +++ b/examples/http/go.sum @@ -40,14 +40,18 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= diff --git a/go.mod b/go.mod index 51209eb8..459c656e 100644 --- a/go.mod +++ b/go.mod @@ -25,32 +25,40 @@ require ( github.com/pkg/errors v0.9.1 github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 + go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/sdk/metric v1.27.0 go.uber.org/atomic v1.11.0 - google.golang.org/grpc v1.63.2 + google.golang.org/grpc v1.64.0 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 gopkg.in/yaml.v2 v2.4.0 ) require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect - google.golang.org/protobuf v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/protobuf v1.34.1 // indirect ) require ( github.com/stretchr/objx v0.5.2 // indirect - go.opentelemetry.io/otel v1.25.0 - go.opentelemetry.io/otel/sdk v1.25.0 - go.opentelemetry.io/otel/trace v1.25.0 - golang.org/x/sys v0.19.0 // indirect + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/trace v1.27.0 + golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6efbded8..038804fb 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYh github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.1.2/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= @@ -14,50 +16,73 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 h1:Oyzwjp7RN7X8q3K4iK1B5+XmQL2ou993u9CGXOBkEpk= github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6/go.mod h1:CN4fCYBnxyOJlBV0CYNXLz6lzNH8SCfNqcCBbpai76c= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/README.md b/instrumentation/github.com/aws/aws-lambda-go/swolambda/README.md new file mode 100644 index 00000000..d2a68da6 --- /dev/null +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/README.md @@ -0,0 +1,51 @@ +# Instrumentation for AWS Lambda + +This package instruments the AWS Lambda `Handler` interface. + +## Usage + +### Add the `Otelcol` extension layer + +Follow the [SolarWinds Observability +documentation](https://documentation.solarwinds.com/en/success_center/observability/content/intro/services/aws-lambda-overview.htm) +to add the Otelcol extension layer. + +**Note**: Unlike other languages, Golang does not require an additional +extension, so the "Instrumentation extension" section on that page does not +apply. + +### Modify your code + +First, install the dependency: +```shell +go get -u github.com/solarwinds/apm-go/instrumentation/github.com/aws/aws-lambda-go/swolambda +``` + +Then, wrap your handler with `swolambda.WrapHandler`: + +```go +package main +import ( + "context" + "github.com/aws/aws-lambda-go/lambda" + "github.com/solarwinds/apm-go/instrumentation/github.com/aws/aws-lambda-go/swolambda" +) + +// Example incoming type +type MyEvent struct {} + +// This is an example handler, yours may have a different signature and a +// different name. It will work ass long as it adheres to what the Lambda SDK +// expects. (See "Valid handler signatures"[0]) +// [0] https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html +func ExampleHandler(ctx context.Context, event *MyEvent) (string, error) { + return "hello world", nil +} +func main() { + // We wrap our handler here and pass the result to `lambda.Start` + lambda.Start(swolambda.WrapHandler(ExampleHandler)) +} +``` + +Now that you've instrumented your code, you should be able to send requests and +see the resulting metrics and traces in SWO. \ No newline at end of file diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod new file mode 100644 index 00000000..88f1dfdf --- /dev/null +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod @@ -0,0 +1,49 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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. + +module github.com/solarwinds/apm-go/instrumentation/github.com/aws/aws-lambda-go/swolambda + +go 1.22.3 + +require ( + github.com/aws/aws-lambda-go v1.47.0 + // TODO this has to be updated to a version that actually has support for Lambda + github.com/solarwinds/apm-go v1.0.0 + go.opentelemetry.io/otel v1.27.0 +) + +require ( + github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect + github.com/aws/smithy-go v1.20.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coocood/freecache v1.2.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.sum b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.sum new file mode 100644 index 00000000..03a92d4f --- /dev/null +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.sum @@ -0,0 +1,66 @@ +github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI= +github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= +github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= +github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/cespare/xxhash/v2 v2.1.2/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/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M= +github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/solarwinds/apm-go v1.0.0 h1:oSF7fRCQE4yhvyZxwQqfFLtCbpqQCWTJrpJ822T/LCo= +github.com/solarwinds/apm-go v1.0.0/go.mod h1:NcXCeVxcj1O6/2UH8iPiWYY9Xd+g+/u04taW3CMw2zg= +github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6 h1:Oyzwjp7RN7X8q3K4iK1B5+XmQL2ou993u9CGXOBkEpk= +github.com/solarwinds/apm-proto v0.0.0-20231107001908-432e697887b6/go.mod h1:CN4fCYBnxyOJlBV0CYNXLz6lzNH8SCfNqcCBbpai76c= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/swolambda.go b/instrumentation/github.com/aws/aws-lambda-go/swolambda/swolambda.go new file mode 100644 index 00000000..e84dad8d --- /dev/null +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/swolambda.go @@ -0,0 +1,101 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 swolambda + +import ( + "context" + "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-lambda-go/lambdacontext" + "github.com/solarwinds/apm-go/internal/config" + "github.com/solarwinds/apm-go/internal/log" + "github.com/solarwinds/apm-go/swo" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" + "go.opentelemetry.io/otel/trace" + "os" + "sync" + "sync/atomic" +) + +var ( + flusher swo.Flusher + tracer trace.Tracer + initHandlerOnce sync.Once + warmStart atomic.Bool +) + +type wrappedHandler struct { + base lambda.Handler + fnName string + txnName string + region string +} + +var _ lambda.Handler = &wrappedHandler{} + +func (w *wrappedHandler) Invoke(ctx context.Context, payload []byte) ([]byte, error) { + // Note: We need to figure out how to determine `faas.trigger` attribute + // which is required by semconv + attrs := []attribute.KeyValue{ + attribute.String("sw.transaction", w.txnName), + semconv.FaaSColdstart(!warmStart.Swap(true)), + semconv.FaaSInvokedName(w.fnName), + semconv.FaaSInvokedProviderAWS, + semconv.FaaSInvokedRegion(w.region), + } + if lc, ok := lambdacontext.FromContext(ctx); !ok { + log.Error("could not obtain lambda context") + } else if lc != nil { + attrs = append(attrs, semconv.FaaSInvocationID(lc.AwsRequestID)) + } + ctx, span := tracer.Start(ctx, w.fnName, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(attrs...)) + defer func() { + span.End() + if flusher != nil { + if err := flusher.Flush(context.Background()); err != nil { + log.Error("could not flush lambda metrics", err) + } + } + }() + res, err := w.base.Invoke(ctx, payload) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err) + } + return res, err +} + +func WrapHandler(f interface{}) lambda.Handler { + initHandlerOnce.Do(func() { + var err error + if flusher, err = swo.StartLambda(lambdacontext.LogStreamName); err != nil { + log.Error("could not initialize SWO lambda instrumentation", err) + } + tracer = otel.GetTracerProvider().Tracer("swolambda") + }) + fnName := os.Getenv("AWS_LAMBDA_FUNCTION_NAME") + txnName := config.GetTransactionName() + if txnName == "" { + txnName = fnName + } + return &wrappedHandler{ + base: lambda.NewHandler(f), + fnName: fnName, + txnName: txnName, + region: os.Getenv("AWS_REGION"), + } +} diff --git a/internal/config/config.go b/internal/config/config.go index c9ef73d0..2b855daf 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,6 +11,7 @@ // 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 config is responsible for loading the configuration from various // sources, e.g., environment variables, configuration files and user input. // It also accepts dynamic settings from the collector server. @@ -340,8 +341,8 @@ const ( may be different from your setting.` ) -// hasLambdaEnv checks if the AWS Lambda env var is set. -func hasLambdaEnv() bool { +// HasLambdaEnv checks if the AWS Lambda env var is set. +func HasLambdaEnv() bool { return os.Getenv("AWS_LAMBDA_FUNCTION_NAME") != "" && os.Getenv("LAMBDA_TASK_ROOT") != "" } @@ -362,12 +363,12 @@ func (c *Config) validate() error { c.Ec2MetadataTimeout = t } - if c.TransactionName != "" && !hasLambdaEnv() { + if c.TransactionName != "" && !HasLambdaEnv() { log.Info(InvalidEnv("TransactionName", c.TransactionName)) c.TransactionName = getFieldDefaultValue(c, "TransactionName") } - if !hasLambdaEnv() { + if !HasLambdaEnv() { if c.ServiceKey != "" { c.ServiceKey = ToServiceKey(c.ServiceKey) if ok := IsValidServiceKey(c.ServiceKey); !ok { @@ -899,7 +900,7 @@ func (c *Config) GetTransactionFiltering() []TransactionFilter { func (c *Config) GetTransactionName() string { c.RLock() defer c.RUnlock() - return c.TransactionName + return strings.TrimSpace(c.TransactionName) } // GetSQLSanitize returns the SQL sanitization level. diff --git a/internal/entryspans/entryspans.go b/internal/entryspans/entryspans.go index 29a0cce9..fc242d0f 100644 --- a/internal/entryspans/entryspans.go +++ b/internal/entryspans/entryspans.go @@ -17,34 +17,62 @@ package entryspans import ( "fmt" "github.com/pkg/errors" + "github.com/solarwinds/apm-go/internal/config" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "sync" ) var ( - state = &entrySpans{ - spans: make(map[trace.TraceID][]*entrySpan), - } + state = makeManagerFromEnv() - NotEntrySpan = errors.New("span is not an entry span") + NotEntrySpan = errors.New("span is not an entry span") + CannotSetTransactionName = errors.New("cannot set transaction, likely due to lambda environment") nullSpanID = trace.SpanID{} nullEntrySpan = &entrySpan{spanId: nullSpanID} ) +type manager interface { + push(tid trace.TraceID, sid trace.SpanID) + delete(tid trace.TraceID, sid trace.SpanID) error + current(tid trace.TraceID) (*entrySpan, bool) + setTransactionName(tid trace.TraceID, name string) error +} + type entrySpan struct { spanId trace.SpanID txnName string } -type entrySpans struct { +type stdManager struct { mut sync.RWMutex spans map[trace.TraceID][]*entrySpan } -func (e *entrySpans) push(tid trace.TraceID, sid trace.SpanID) { +type noopManager struct{} + +func (n noopManager) push(trace.TraceID, trace.SpanID) {} + +func (n noopManager) delete(trace.TraceID, trace.SpanID) error { + return nil +} + +func (n noopManager) current(trace.TraceID) (*entrySpan, bool) { + return nil, false +} + +func (n noopManager) setTransactionName(trace.TraceID, string) error { + return CannotSetTransactionName +} + +var ( + _ manager = &stdManager{} + _ manager = &noopManager{} +) + +func (e *stdManager) push(tid trace.TraceID, sid trace.SpanID) { e.mut.Lock() defer e.mut.Unlock() var list []*entrySpan @@ -56,14 +84,14 @@ func (e *entrySpans) push(tid trace.TraceID, sid trace.SpanID) { e.spans[tid] = list } -func (e *entrySpans) current(tid trace.TraceID) (*entrySpan, bool) { +func (e *stdManager) current(tid trace.TraceID) (*entrySpan, bool) { e.mut.Lock() defer e.mut.Unlock() a, ok := e.currentUnsafe(tid) return a, ok } -func (e *entrySpans) currentUnsafe(tid trace.TraceID) (*entrySpan, bool) { +func (e *stdManager) currentUnsafe(tid trace.TraceID) (*entrySpan, bool) { if list, ok := e.spans[tid]; ok { l := len(list) if len(list) == 0 { @@ -85,7 +113,7 @@ func Push(span sdktrace.ReadOnlySpan) error { return nil } -func (e *entrySpans) delete(tid trace.TraceID, sid trace.SpanID) error { +func (e *stdManager) delete(tid trace.TraceID, sid trace.SpanID) error { e.mut.Lock() defer e.mut.Unlock() @@ -125,7 +153,7 @@ func Current(tid trace.TraceID) (trace.SpanID, bool) { return curr.spanId, ok } -func (e *entrySpans) setTransactionName(tid trace.TraceID, name string) error { +func (e *stdManager) setTransactionName(tid trace.TraceID, name string) error { e.mut.Lock() defer e.mut.Unlock() @@ -152,3 +180,15 @@ func IsEntrySpan(span sdktrace.ReadOnlySpan) bool { parent := span.Parent() return !parent.IsValid() || parent.IsRemote() } + +func makeManagerFromEnv() manager { + if config.HasLambdaEnv() { + // In Lambda, we cannot modify the outgoing spans for transaction naming, + // thus we do not want to track entry spans. + return &noopManager{} + } else { + return &stdManager{ + spans: make(map[trace.TraceID][]*entrySpan), + } + } +} diff --git a/internal/entryspans/entryspans_test.go b/internal/entryspans/entryspans_test.go index e8823da1..97242926 100644 --- a/internal/entryspans/entryspans_test.go +++ b/internal/entryspans/entryspans_test.go @@ -33,7 +33,7 @@ var ( span4 = trace.SpanID{0x4} ) -func (e *entrySpans) pop(tid trace.TraceID) (trace.SpanID, bool) { +func (e *stdManager) pop(tid trace.TraceID) (trace.SpanID, bool) { e.mut.Lock() defer e.mut.Unlock() @@ -56,7 +56,14 @@ func (e *entrySpans) pop(tid trace.TraceID) (trace.SpanID, bool) { } } +func (e *stdManager) reset() { + e.mut.Lock() + defer e.mut.Unlock() + clear(e.spans) +} + func TestCurrent(t *testing.T) { + state := state.(*stdManager) sid, ok := Current(traceA) require.False(t, ok) require.False(t, sid.IsValid()) @@ -109,6 +116,7 @@ func TestCurrent(t *testing.T) { } func TestPush(t *testing.T) { + state := state.(*stdManager) var err error tr, teardown := testutils.TracerSetup() defer teardown() @@ -132,8 +140,8 @@ func TestPush(t *testing.T) { } func TestSetTransactionName(t *testing.T) { - // reset state - state = &entrySpans{spans: make(map[trace.TraceID][]*entrySpan)} + state := state.(*stdManager) + state.reset() err := SetTransactionName(traceA, "foo bar") require.Error(t, err) @@ -173,8 +181,8 @@ func TestSetTransactionName(t *testing.T) { } func TestDelete(t *testing.T) { - // reset state - state = &entrySpans{spans: make(map[trace.TraceID][]*entrySpan)} + state := state.(*stdManager) + state.reset() err := state.delete(traceA, span1) require.Error(t, err) diff --git a/internal/exporter/exporter.go b/internal/exporter/exporter.go index f950ff8d..e88d13ca 100644 --- a/internal/exporter/exporter.go +++ b/internal/exporter/exporter.go @@ -22,7 +22,7 @@ import ( "github.com/solarwinds/apm-go/internal/log" "github.com/solarwinds/apm-go/internal/reporter" "github.com/solarwinds/apm-go/internal/swotel/semconv" - "github.com/solarwinds/apm-go/internal/utils" + "github.com/solarwinds/apm-go/internal/txn" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -45,7 +45,7 @@ func (e *exporter) exportSpan(_ context.Context, s sdktrace.ReadOnlySpan) { attribute.String("otel.scope.version", s.InstrumentationScope().Version), }) if entryspans.IsEntrySpan(s) { - evt.AddKV(attribute.String("TransactionName", utils.GetTransactionName(s))) + evt.AddKV(attribute.String("TransactionName", txn.GetTransactionName(s))) // We MUST clear the entry span here. The SpanProcessor only clears entry spans when they are `RecordOnly` if err := entryspans.Delete(s); err != nil { log.Warningf( diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index 5ea6b4fc..e3e0d15a 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -381,7 +381,7 @@ func TestAddHistogramToBSON(t *testing.T) { } func TestGenerateMetricsMessage(t *testing.T) { - reg := NewLegacyRegistry().(*registry) + reg := NewLegacyRegistry(false).(*registry) flushInterval := int32(60) bbuf := bson.WithBuf(reg.BuildBuiltinMetricsMessage(flushInterval, &EventQueueStats{}, map[string]*RateCounts{ // requested, sampled, limited, traced, through @@ -467,7 +467,7 @@ func TestGenerateMetricsMessage(t *testing.T) { assert.Nil(t, m["TransactionNameOverflow"]) - reg = NewLegacyRegistry().(*registry) + reg = NewLegacyRegistry(false).(*registry) for i := 0; i <= metricsTransactionsMaxDefault; i++ { if !reg.apmMetrics.txnMap.isWithinLimit("Transaction-" + strconv.Itoa(i)) { break @@ -546,9 +546,9 @@ func TestRecordSpan(t *testing.T) { ), ) span.End(trace.WithTimestamp(now.Add(1 * time.Second))) - reg := NewLegacyRegistry().(*registry) + reg := NewLegacyRegistry(false).(*registry) - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), false) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) m := reg.apmMetrics.CopyAndReset(60) assert.NotEmpty(t, m.m) @@ -567,7 +567,6 @@ func TestRecordSpan(t *testing.T) { assert.Equal(t, responseTime, v.Name) h := reg.apmHistograms.histograms - reg = NewLegacyRegistry().(*registry) assert.NotEmpty(t, h) globalHisto := h[""] granularHisto := h["my cool route"] @@ -579,8 +578,9 @@ func TestRecordSpan(t *testing.T) { assert.Equal(t, 1.001472e+06, granularHisto.hist.Mean()) assert.Equal(t, int64(1), granularHisto.hist.TotalCount()) + reg = NewLegacyRegistry(true).(*registry) // Now test for AO - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), true) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) m = reg.apmMetrics.CopyAndReset(60) assert.NotEmpty(t, m.m) @@ -637,8 +637,8 @@ func TestRecordSpanErrorStatus(t *testing.T) { ) span.End(trace.WithTimestamp(now.Add(1 * time.Second))) - reg := NewLegacyRegistry().(*registry) - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), false) + reg := NewLegacyRegistry(false).(*registry) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) m := reg.apmMetrics.CopyAndReset(60) assert.NotEmpty(t, m.m) @@ -657,7 +657,6 @@ func TestRecordSpanErrorStatus(t *testing.T) { assert.Equal(t, responseTime, v.Name) h := reg.apmHistograms.histograms - reg = NewLegacyRegistry().(*registry) assert.NotEmpty(t, h) globalHisto := h[""] granularHisto := h["my cool route"] @@ -670,7 +669,8 @@ func TestRecordSpanErrorStatus(t *testing.T) { assert.Equal(t, int64(1), granularHisto.hist.TotalCount()) // Now test for AO - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), true) + reg = NewLegacyRegistry(true).(*registry) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) m = reg.apmMetrics.CopyAndReset(60) assert.NotEmpty(t, m.m) @@ -740,14 +740,14 @@ func TestRecordSpanOverflow(t *testing.T) { ) span2.End(trace.WithTimestamp(now.Add(1 * time.Second))) - reg := NewLegacyRegistry().(*registry) + reg := NewLegacyRegistry(false).(*registry) // The cap only takes affect after the following reset reg.SetApmMetricsCap(1) reg.apmMetrics.CopyAndReset(60) assert.Equal(t, int32(1), reg.ApmMetricsCap()) - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), false) - reg.RecordSpan(span2.(sdktrace.ReadOnlySpan), false) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) + reg.RecordSpan(span2.(sdktrace.ReadOnlySpan)) m := reg.apmMetrics.CopyAndReset(60) // We expect to have a record for `my cool route` and one for `other` @@ -826,13 +826,13 @@ func TestRecordSpanOverflowAppoptics(t *testing.T) { // The cap only takes affect after the following reset // Appoptics-style will generate 3 metrics, so we'll set the cap to that here - reg := NewLegacyRegistry().(*registry) + reg := NewLegacyRegistry(true).(*registry) reg.SetApmMetricsCap(3) reg.apmMetrics.CopyAndReset(60) assert.Equal(t, int32(3), reg.ApmMetricsCap()) - reg.RecordSpan(span.(sdktrace.ReadOnlySpan), true) - reg.RecordSpan(span2.(sdktrace.ReadOnlySpan), true) + reg.RecordSpan(span.(sdktrace.ReadOnlySpan)) + reg.RecordSpan(span2.(sdktrace.ReadOnlySpan)) m := reg.apmMetrics.CopyAndReset(60) // We expect to have 3 records for `my cool route` and 3 for `other` diff --git a/internal/metrics/otel.go b/internal/metrics/otel.go new file mode 100644 index 00000000..083f5ba0 --- /dev/null +++ b/internal/metrics/otel.go @@ -0,0 +1,77 @@ +// © 2024 SolarWinds Worldwide, LLC. All rights reserved. +// +// 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 metrics + +import ( + "context" + "github.com/solarwinds/apm-go/internal/txn" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" + "go.opentelemetry.io/otel/trace" +) + +type otelRegistry struct { + histo metric.Int64Histogram +} + +var searchSet = map[attribute.Key]bool{ + semconv.HTTPMethodKey: true, + semconv.HTTPStatusCodeKey: true, + semconv.HTTPRouteKey: true, +} + +func (o *otelRegistry) RecordSpan(span sdktrace.ReadOnlySpan) { + var attrs = []attribute.KeyValue{ + attribute.Bool("sw.is_error", span.Status().Code == codes.Error), + attribute.String("sw.transaction", txn.GetTransactionName(span)), + } + if span.SpanKind() == trace.SpanKindServer { + for _, attr := range span.Attributes() { + if searchSet[attr.Key] { + attrs = append(attrs, attr) + } + } + } + duration := span.EndTime().Sub(span.StartTime()) + o.histo.Record( + context.Background(), + duration.Milliseconds(), + metric.WithAttributes(attrs...), + ) +} + +var _ MetricRegistry = &otelRegistry{} + +func NewOtelRegistry(p metric.MeterProvider) (MetricRegistry, error) { + meter := p.Meter("sw.apm.request.metrics") + if histo, err := meter.Int64Histogram( + "trace.service.response_time", + metric.WithExplicitBucketBoundaries(), + metric.WithUnit("ms"), + ); err != nil { + return nil, err + } else { + return &otelRegistry{histo: histo}, nil + } +} + +func TemporalitySelector(sdkmetric.InstrumentKind) metricdata.Temporality { + return metricdata.DeltaTemporality +} diff --git a/internal/metrics/registry.go b/internal/metrics/registry.go index 4d25db5d..f1fa5c70 100644 --- a/internal/metrics/registry.go +++ b/internal/metrics/registry.go @@ -19,7 +19,7 @@ import ( "github.com/solarwinds/apm-go/internal/bson" "github.com/solarwinds/apm-go/internal/log" "github.com/solarwinds/apm-go/internal/swotel/semconv" - "github.com/solarwinds/apm-go/internal/utils" + "github.com/solarwinds/apm-go/internal/txn" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/trace" trace2 "go.opentelemetry.io/otel/trace" @@ -31,11 +31,12 @@ type registry struct { apmHistograms *histograms apmMetrics *measurements customMetrics *measurements + isAppoptics bool } var _ LegacyRegistry = ®istry{} -func NewLegacyRegistry() LegacyRegistry { +func NewLegacyRegistry(isAppoptics bool) LegacyRegistry { return ®istry{ apmHistograms: &histograms{ histograms: make(map[string]*histogram), @@ -43,11 +44,12 @@ func NewLegacyRegistry() LegacyRegistry { }, apmMetrics: newMeasurements(false, metricsTransactionsMaxDefault), customMetrics: newMeasurements(true, metricsCustomMetricsMaxDefault), + isAppoptics: isAppoptics, } } type MetricRegistry interface { - RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) + RecordSpan(span trace.ReadOnlySpan) } type LegacyRegistry interface { @@ -163,7 +165,7 @@ func (r *registry) BuildBuiltinMetricsMessage(flushInterval int32, qs *EventQueu return bbuf.GetBuf() } -func (r *registry) RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) { +func (r *registry) RecordSpan(span trace.ReadOnlySpan) { method := "" status := int64(0) isError := span.Status().Code == codes.Error @@ -192,7 +194,7 @@ func (r *registry) RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) { } swoTags["sw.is_error"] = strconv.FormatBool(isError) - txnName := utils.GetTransactionName(span) + txnName := txn.GetTransactionName(span) swoTags["sw.transaction"] = txnName duration := span.EndTime().Sub(span.StartTime()) @@ -207,7 +209,7 @@ func (r *registry) RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) { var tagsList []map[string]string var metricName string - if !isAppoptics { + if !r.isAppoptics { tagsList = []map[string]string{swoTags} metricName = responseTime } else { @@ -217,7 +219,7 @@ func (r *registry) RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) { r.apmHistograms.recordHistogram("", duration) if err := s.processMeasurements(metricName, tagsList, r.apmMetrics); errors.Is(err, ErrExceedsMetricsCountLimit) { - if isAppoptics { + if r.isAppoptics { s.Transaction = OtherTransactionName tagsList = s.appOpticsTagsList() } else { diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index add94858..8184d298 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -15,13 +15,18 @@ package oboe import ( + "context" + "os" "time" "github.com/solarwinds/apm-go/internal/log" ) const ( - settingsCheckSeconds = 10 + settingsCheckDuration = 10 * time.Second + settingsFileName = "/tmp/solarwinds-apm-settings.json" + + timeoutEnv = "SW_APM_INITIAL_SETTINGS_FILE_TIMEOUT" ) var exit = make(chan bool, 1) @@ -33,9 +38,9 @@ type FileBasedWatcher interface { // NewFileBasedWatcher returns a FileBasedWatcher that periodically // reads lambda settings from file -func NewFileBasedWatcher(oboe *Oboe) FileBasedWatcher { +func NewFileBasedWatcher(oboe Oboe) FileBasedWatcher { return &fileBasedWatcher{ - *oboe, + oboe, } } @@ -44,22 +49,34 @@ type fileBasedWatcher struct { } // readSettingFromFile parses, normalizes, and print settings from file -func (fbw *fileBasedWatcher) readSettingFromFile() { - settingLambda, err := newSettingLambdaFromFile() - if err != nil { +func (w *fileBasedWatcher) readSettingFromFile() { + s, err := newSettingLambdaFromFile() + if os.IsNotExist(err) { + log.Debug("Settings file does not yet exist") + return + } else if err != nil { log.Errorf("Could not read setting from file: %s", err) return } log.Debugf( - "Got lambda settings from file:\n%v", - settingLambda, + "Got lambda settings from file:\n%+v", + s, + ) + w.o.UpdateSetting( + int32(s.sType), + s.layer, + s.flags, + s.value, + s.ttl, + s.args, ) } // Start runs a ticker that checks settings expiry from cache // and, if expired, updates cache and oboe settings. -func (fbw *fileBasedWatcher) Start() { - ticker := time.NewTicker(settingsCheckSeconds * time.Second) +func (w *fileBasedWatcher) Start() { + ticker := time.NewTicker(settingsCheckDuration) + waitForSettingsFile() go func() { defer ticker.Stop() for { @@ -67,13 +84,56 @@ func (fbw *fileBasedWatcher) Start() { case <-exit: return case <-ticker.C: - fbw.readSettingFromFile() + w.readSettingFromFile() } } }() + w.readSettingFromFile() } -func (fbw *fileBasedWatcher) Stop() { +func (w *fileBasedWatcher) Stop() { log.Info("Stopping settings file watcher.") exit <- true } + +func waitForSettingsFile() { + var timeout = 1 * time.Second + if timeoutStr := os.Getenv(timeoutEnv); timeoutStr != "" { + if override, err := time.ParseDuration(timeoutStr); err != nil { + log.Errorf("could not parse duration from %s '%s': %s", timeoutEnv, timeoutStr, err) + } else if int64(override) < 1 { + log.Infof("%s was 0 or negative, skipping wait for settings file", timeoutEnv) + return + } else { + timeout = override + } + } + log.Debugf("Waiting for settings file for up to %s (override with %s; set to 0 to skip)", timeout, timeoutEnv) + // We could use something like fsnotify, but that's overkill for something this simple + waitTicker := time.NewTicker(10 * time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + defer waitTicker.Stop() + for { + select { + case <-waitTicker.C: + { + _, err := os.Stat(settingsFileName) + if err == nil { + log.Info("Settings file found") + return + } else if os.IsNotExist(err) { + log.Debug("Settings file does not yet exist") + } else { + log.Errorf("Could not read settings from file: %s", err) + return + } + } + case <-ctx.Done(): + { + log.Info("timed out waiting for settings file") + return + } + } + } +} diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index b574ed8a..365ffa58 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -44,7 +44,7 @@ type settingArguments struct { } type settingLambdaNormalized struct { - sType int32 + sType settingType layer string flags []byte value int64 @@ -71,11 +71,11 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN ) settingNorm := &settingLambdaNormalized{ - 1, // always DEFAULT_SAMPLE_RATE - "", // not set since type is always DEFAULT_SAMPLE_RATE + TypeDefault, // always DEFAULT_SAMPLE_RATE + "", // not set since type is always DEFAULT_SAMPLE_RATE flags, - int64(fromFile.Value), - int64(fromFile.Ttl), + fromFile.Value, + fromFile.Ttl, args, } @@ -86,7 +86,7 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN // specific path in a specific format then returns values normalized for // oboe UpdateSetting, else returns error. func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { - settingFile, err := os.Open("/tmp/solarwinds-apm-settings.json") + settingFile, err := os.Open(settingsFileName) if err != nil { return nil, err } @@ -95,8 +95,8 @@ func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { return nil, err } // Settings file should be an array with a single settings object - var settingLambdas []settingLambdaFromFile - if err := json.Unmarshal(settingBytes, &settingLambdas); err != nil { + var settingLambdas []*settingLambdaFromFile + if err = json.Unmarshal(settingBytes, &settingLambdas); err != nil { return nil, err } if len(settingLambdas) != 1 { @@ -105,5 +105,5 @@ func newSettingLambdaFromFile() (*settingLambdaNormalized, error) { settingLambda := settingLambdas[0] - return newSettingLambdaNormalized(&settingLambda), nil + return newSettingLambdaNormalized(settingLambda), nil } diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go index 67e075ad..cf41686a 100644 --- a/internal/oboe/settings_lambda_test.go +++ b/internal/oboe/settings_lambda_test.go @@ -23,8 +23,6 @@ import ( "github.com/stretchr/testify/require" ) -const SettingsFile = "/tmp/solarwinds-apm-settings.json" - func TestNewSettingLambdaNormalized(t *testing.T) { settingArgs := settingArguments{ 1, @@ -46,7 +44,7 @@ func TestNewSettingLambdaNormalized(t *testing.T) { } result := newSettingLambdaNormalized(&fromFile) - assert.Equal(t, int32(1), result.sType) + assert.Equal(t, TypeDefault, result.sType) assert.Equal(t, "", result.layer) assert.Equal( t, @@ -103,44 +101,44 @@ func TestNewSettingLambdaNormalized(t *testing.T) { } func TestNewSettingLambdaFromFileErrorOpen(t *testing.T) { - require.NoFileExists(t, SettingsFile) + require.NoFileExists(t, settingsFileName) res, err := newSettingLambdaFromFile() assert.Nil(t, res) assert.Error(t, err) } func TestNewSettingLambdaFromFileErrorUnmarshal(t *testing.T) { - require.NoFileExists(t, SettingsFile) + require.NoFileExists(t, settingsFileName) content := []byte("hello\ngo\n") - require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) + require.NoError(t, os.WriteFile(settingsFileName, content, 0644)) res, err := newSettingLambdaFromFile() assert.Nil(t, res) assert.Error(t, err) - os.Remove(SettingsFile) + os.Remove(settingsFileName) } func TestNewSettingLambdaFromFileErrorLen(t *testing.T) { - require.NoFileExists(t, SettingsFile) + require.NoFileExists(t, settingsFileName) content := []byte("[]") - require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) + require.NoError(t, os.WriteFile(settingsFileName, content, 0644)) res, err := newSettingLambdaFromFile() assert.Nil(t, res) assert.Error(t, err) - os.Remove(SettingsFile) + os.Remove(settingsFileName) } func TestNewSettingLambdaFromFile(t *testing.T) { - require.NoFileExists(t, SettingsFile) + require.NoFileExists(t, settingsFileName) content := []byte("[{\"arguments\":{\"BucketCapacity\":1,\"BucketRate\":1,\"MetricsFlushInterval\":1,\"TriggerRelaxedBucketCapacity\":1,\"TriggerRelaxedBucketRate\":1,\"TriggerStrictBucketCapacity\":1,\"TriggerStrictBucketRate\":1},\"flags\":\"SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE\",\"layer\":\"\",\"timestamp\":1715900164,\"ttl\":120,\"type\":0,\"value\":1000000}]") - require.NoError(t, os.WriteFile(SettingsFile, content, 0644)) + require.NoError(t, os.WriteFile(settingsFileName, content, 0644)) result, err := newSettingLambdaFromFile() assert.Nil(t, err) - assert.Equal(t, int32(1), result.sType) + assert.Equal(t, TypeDefault, result.sType) assert.Equal(t, "", result.layer) assert.Equal( t, @@ -195,5 +193,5 @@ func TestNewSettingLambdaFromFile(t *testing.T) { result.args[constants.KvSignatureKey], ) - os.Remove(SettingsFile) + os.Remove(settingsFileName) } diff --git a/internal/processor/processor.go b/internal/processor/processor.go index f581d389..de7a0118 100644 --- a/internal/processor/processor.go +++ b/internal/processor/processor.go @@ -22,18 +22,16 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" ) -func NewInboundMetricsSpanProcessor(registry metrics.MetricRegistry, isAppoptics bool) sdktrace.SpanProcessor { +func NewInboundMetricsSpanProcessor(registry metrics.MetricRegistry) sdktrace.SpanProcessor { return &inboundMetricsSpanProcessor{ - registry: registry, - isAppoptics: isAppoptics, + registry: registry, } } var _ sdktrace.SpanProcessor = &inboundMetricsSpanProcessor{} type inboundMetricsSpanProcessor struct { - registry metrics.MetricRegistry - isAppoptics bool + registry metrics.MetricRegistry } func (s *inboundMetricsSpanProcessor) OnStart(_ context.Context, span sdktrace.ReadWriteSpan) { @@ -62,7 +60,7 @@ func maybeClearEntrySpan(span sdktrace.ReadOnlySpan) { func (s *inboundMetricsSpanProcessor) OnEnd(span sdktrace.ReadOnlySpan) { if entryspans.IsEntrySpan(span) { - s.registry.RecordSpan(span, s.isAppoptics) + s.registry.RecordSpan(span) maybeClearEntrySpan(span) } } diff --git a/internal/processor/processor_test.go b/internal/processor/processor_test.go index c6edba46..704e33b0 100644 --- a/internal/processor/processor_test.go +++ b/internal/processor/processor_test.go @@ -26,14 +26,12 @@ import ( ) type recordMock struct { - span sdktrace.ReadOnlySpan - isAppoptics bool - called bool + span sdktrace.ReadOnlySpan + called bool } -func (r *recordMock) RecordSpan(span sdktrace.ReadOnlySpan, isAppoptics bool) { +func (r *recordMock) RecordSpan(span sdktrace.ReadOnlySpan) { r.span = span - r.isAppoptics = isAppoptics r.called = true } @@ -65,7 +63,7 @@ var _ metrics.LegacyRegistry = &recordMock{} func TestInboundMetricsSpanProcessorOnEnd(t *testing.T) { mock := &recordMock{} - sp := NewInboundMetricsSpanProcessor(mock, false) + sp := NewInboundMetricsSpanProcessor(mock) tp := sdktrace.NewTracerProvider( sdktrace.WithSpanProcessor(sp), sdktrace.WithSampler(sdktrace.AlwaysSample()), @@ -86,7 +84,6 @@ func TestInboundMetricsSpanProcessorOnEnd(t *testing.T) { require.True(t, ok) require.Equal(t, s.SpanContext().SpanID(), es) assert.True(t, mock.called) - assert.False(t, mock.isAppoptics) } type recordOnlySampler struct{} @@ -104,7 +101,7 @@ func (ro recordOnlySampler) Description() string { func TestInboundMetricsSpanProcessorOnEndRecordOnly(t *testing.T) { mock := &recordMock{} - sp := NewInboundMetricsSpanProcessor(mock, false) + sp := NewInboundMetricsSpanProcessor(mock) tp := sdktrace.NewTracerProvider( sdktrace.WithSpanProcessor(sp), sdktrace.WithSampler(recordOnlySampler{}), @@ -125,12 +122,11 @@ func TestInboundMetricsSpanProcessorOnEndRecordOnly(t *testing.T) { require.False(t, ok) require.False(t, es.IsValid()) assert.True(t, mock.called) - assert.False(t, mock.isAppoptics) } func TestInboundMetricsSpanProcessorOnEndWithLocalParent(t *testing.T) { mock := &recordMock{} - sp := NewInboundMetricsSpanProcessor(mock, false) + sp := NewInboundMetricsSpanProcessor(mock) tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sp)) tracer := tp.Tracer("foo") ctx, s1 := tracer.Start(context.Background(), "span name") @@ -153,7 +149,7 @@ func TestInboundMetricsSpanProcessorOnEndWithLocalParent(t *testing.T) { func TestInboundMetricsSpanProcessorOnEndWithRemoteParent(t *testing.T) { mock := &recordMock{} - sp := NewInboundMetricsSpanProcessor(mock, false) + sp := NewInboundMetricsSpanProcessor(mock) tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sp)) tracer := tp.Tracer("foo") ctx := context.Background() diff --git a/internal/reporter/reporter_grpc.go b/internal/reporter/reporter_grpc.go index b0ced062..af102ea1 100644 --- a/internal/reporter/reporter_grpc.go +++ b/internal/reporter/reporter_grpc.go @@ -1280,7 +1280,7 @@ func (d *DefaultDialer) Dial(p DialParams) (*grpc.ClientConn, error) { opts = append(opts, grpc.WithContextDialer(newGRPCProxyDialer(p))) } - return grpc.Dial(p.Address, opts...) + return grpc.NewClient(p.Address, opts...) } func newGRPCProxyDialer(p DialParams) func(context.Context, string) (net.Conn, error) { diff --git a/internal/reporter/reporter_test.go b/internal/reporter/reporter_test.go index b187dc10..f3c1548d 100644 --- a/internal/reporter/reporter_test.go +++ b/internal/reporter/reporter_test.go @@ -89,7 +89,7 @@ func TestGRPCReporter(t *testing.T) { setEnv("SW_APM_COLLECTOR", addr) setEnv("SW_APM_TRUSTEDPATH", testCertFile) config.Load() - registry := metrics.NewLegacyRegistry() + registry := metrics.NewLegacyRegistry(false) o := oboe.NewOboe() r := newGRPCReporter("myservice", registry, o).(*grpcReporter) @@ -176,7 +176,7 @@ func TestShutdownGRPCReporter(t *testing.T) { setEnv("SW_APM_COLLECTOR", addr) setEnv("SW_APM_TRUSTEDPATH", testCertFile) config.Load() - registry := metrics.NewLegacyRegistry() + registry := metrics.NewLegacyRegistry(false) o := oboe.NewOboe() r := newGRPCReporter("myservice", registry, o).(*grpcReporter) r.ShutdownNow() @@ -236,7 +236,7 @@ func TestInvalidKey(t *testing.T) { config.Load() log.SetLevel(log.INFO) - registry := metrics.NewLegacyRegistry() + registry := metrics.NewLegacyRegistry(false) o := oboe.NewOboe() r := newGRPCReporter("myservice", registry, o).(*grpcReporter) @@ -447,7 +447,7 @@ func TestInitReporter(t *testing.T) { // Test disable agent setEnv("SW_APM_ENABLED", "false") config.Load() - registry := metrics.NewLegacyRegistry() + registry := metrics.NewLegacyRegistry(false) o := oboe.NewOboe() r := initReporter(resource.Empty(), registry, o) require.IsType(t, &nullReporter{}, r) @@ -493,7 +493,7 @@ func testProxy(t *testing.T, proxyUrl string) { server := StartTestGRPCServer(t, addr) time.Sleep(100 * time.Millisecond) - registry := metrics.NewLegacyRegistry() + registry := metrics.NewLegacyRegistry(false) o := oboe.NewOboe() r := newGRPCReporter("myservice", registry, o).(*grpcReporter) diff --git a/internal/utils/otel.go b/internal/txn/txn.go similarity index 65% rename from internal/utils/otel.go rename to internal/txn/txn.go index 9e10eaf0..45b8a6ed 100644 --- a/internal/utils/otel.go +++ b/internal/txn/txn.go @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package utils +package txn import ( + "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/entryspans" "github.com/solarwinds/apm-go/internal/swotel/semconv" "go.opentelemetry.io/otel/attribute" @@ -35,32 +36,36 @@ func GetTransactionName(span sdktrace.ReadOnlySpan) string { // deriveTransactionName returns transaction name from given span name and attributes, falling back to "unknown" func deriveTransactionName(name string, attrs []attribute.KeyValue) string { - var httpRoute, httpUrl, txnName = "", "", "" - for _, attr := range attrs { - if attr.Key == semconv.HTTPRouteKey { - httpRoute = attr.Value.AsString() - } else if attr.Key == semconv.HTTPURLKey { - httpUrl = attr.Value.AsString() + txnName := config.GetTransactionName() + if txnName == "" { + var httpRoute, httpUrl = "", "" + for _, attr := range attrs { + if attr.Key == semconv.HTTPRouteKey { + httpRoute = attr.Value.AsString() + } else if attr.Key == semconv.HTTPURLKey { + httpUrl = attr.Value.AsString() + } } - } - if httpRoute != "" { - txnName = httpRoute - } else if name != "" { - txnName = name - } - if httpUrl != "" && strings.TrimSpace(txnName) == "" { - parsed, err := url.Parse(httpUrl) - if err != nil { - // We can't import internal logger in the util package, so we default to "log". However, this should be - // infrequent. - log.Println("could not parse URL from span", httpUrl) - } else { - // Clear user/password - parsed.User = nil - txnName = parsed.String() + if httpRoute != "" { + txnName = httpRoute + } else if name != "" { + txnName = name + } + if httpUrl != "" && strings.TrimSpace(txnName) == "" { + parsed, err := url.Parse(httpUrl) + if err != nil { + // We can't import internal logger in the util package, so we default to "log". However, this should be + // infrequent. + log.Println("could not parse URL from span", httpUrl) + } else { + // Clear user/password + parsed.User = nil + txnName = parsed.String() + } } } + txnName = strings.TrimSpace(txnName) if txnName == "" { txnName = "unknown" diff --git a/internal/utils/otel_test.go b/internal/txn/txn_test.go similarity index 66% rename from internal/utils/otel_test.go rename to internal/txn/txn_test.go index aaf79e73..b1ddbd1b 100644 --- a/internal/utils/otel_test.go +++ b/internal/txn/txn_test.go @@ -12,15 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -package utils +package txn import ( "context" + "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/entryspans" "github.com/solarwinds/apm-go/internal/testutils" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/trace" + "os" "strings" "testing" ) @@ -87,3 +89,42 @@ func TestDeriveTransactionName(t *testing.T) { expected := strings.Repeat("a", 255) require.Equal(t, expected, deriveTransactionName(name, attrs)) } + +func TestDeriveTxnFromEnv(t *testing.T) { + envTxn := "env-provided" + name := "span name" + var attrs []attribute.KeyValue + // `SW_APM_TRANSACTION_NAME` only takes effect in Lambda + require.NoError(t, os.Setenv("SW_APM_TRANSACTION_NAME", envTxn)) + config.Load() + require.Equal(t, "", config.GetTransactionName()) + require.Equal(t, "span name", deriveTransactionName(name, attrs)) + + require.NoError(t, os.Setenv("AWS_LAMBDA_FUNCTION_NAME", "foo")) + require.NoError(t, os.Setenv("LAMBDA_TASK_ROOT", "bar")) + defer func() { + _ = os.Unsetenv("SW_APM_TRANSACTION_NAME") + _ = os.Unsetenv("AWS_LAMBDA_FUNCTION_NAME") + _ = os.Unsetenv("LAMBDA_TASK_ROOT") + }() + config.Load() + require.Equal(t, envTxn, config.GetTransactionName()) + require.Equal(t, envTxn, deriveTransactionName(name, attrs)) +} +func TestDeriveTxnFromEnvTruncated(t *testing.T) { + envTxn := strings.Repeat("a", 1024) + expected := strings.Repeat("a", 255) + name := "span name" + var attrs []attribute.KeyValue + require.NoError(t, os.Setenv("SW_APM_TRANSACTION_NAME", envTxn)) + require.NoError(t, os.Setenv("AWS_LAMBDA_FUNCTION_NAME", "foo")) + require.NoError(t, os.Setenv("LAMBDA_TASK_ROOT", "bar")) + defer func() { + _ = os.Unsetenv("SW_APM_TRANSACTION_NAME") + _ = os.Unsetenv("AWS_LAMBDA_FUNCTION_NAME") + _ = os.Unsetenv("LAMBDA_TASK_ROOT") + }() + config.Load() + require.Equal(t, envTxn, config.GetTransactionName()) + require.Equal(t, expected, deriveTransactionName(name, attrs)) +} diff --git a/swo/agent.go b/swo/agent.go index f5846d5e..d9298ffa 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -16,10 +16,9 @@ package swo import ( "context" - "io" - stdlog "log" - "strings" + "os" + "github.com/pkg/errors" "github.com/solarwinds/apm-go/internal/config" "github.com/solarwinds/apm-go/internal/entryspans" "github.com/solarwinds/apm-go/internal/exporter" @@ -30,14 +29,19 @@ import ( "github.com/solarwinds/apm-go/internal/propagator" "github.com/solarwinds/apm-go/internal/reporter" "github.com/solarwinds/apm-go/internal/sampler" + "github.com/solarwinds/apm-go/internal/utils" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" - - "github.com/pkg/errors" + "io" + stdlog "log" + "strings" ) var ( @@ -89,20 +93,21 @@ func Start(resourceAttrs ...attribute.KeyValue) (func(), error) { // return a no-op func so that we don't cause a nil-deref for the end-user }, err } - registry := metrics.NewLegacyRegistry() + isAppoptics := strings.Contains(strings.ToLower(config.GetCollector()), "appoptics.com") + registry := metrics.NewLegacyRegistry(isAppoptics) o := oboe.NewOboe() _reporter, err := reporter.Start(resrc, registry, o) if err != nil { return func() {}, err } + exprtr := exporter.NewExporter(_reporter) smplr, err := sampler.NewSampler(o) if err != nil { return func() {}, err } config.Load() - isAppoptics := strings.Contains(strings.ToLower(config.GetCollector()), "appoptics.com") - proc := processor.NewInboundMetricsSpanProcessor(registry, isAppoptics) + proc := processor.NewInboundMetricsSpanProcessor(registry) prop := propagation.NewCompositeTextMapPropagator( &propagation.TraceContext{}, &propagation.Baggage{}, @@ -137,3 +142,94 @@ func SetTransactionName(ctx context.Context, name string) error { } return entryspans.SetTransactionName(sc.TraceID(), name) } + +type Flusher interface { + Flush(ctx context.Context) error +} + +type lambdaFlusher struct { + Reader *metric.PeriodicReader +} + +func (l lambdaFlusher) Flush(ctx context.Context) error { + return l.Reader.ForceFlush(ctx) +} + +var _ Flusher = &lambdaFlusher{} + +func StartLambda(lambdaLogStreamName string) (Flusher, error) { + // By default, the Go OTEL SDK sets this to `https://localhost:4317`, however + // we do not use https for the local collector in Lambda. We override if not + // already set. + if os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") == "" { + if err := os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); err != nil { + log.Warningf("could not override unset OTEL_EXPORTER_OTLP_ENDPOINT %s", err) + } + } + ctx := context.Background() + o := oboe.NewOboe() + settingsWatcher := oboe.NewFileBasedWatcher(o) + // settingsWatcher is started but never stopped in Lambda + settingsWatcher.Start() + var err error + var tpOpts []sdktrace.TracerProviderOption + var metExp metric.Exporter + if metExp, err = otlpmetricgrpc.New(ctx, + otlpmetricgrpc.WithTemporalitySelector(metrics.TemporalitySelector), + ); err != nil { + return nil, err + } + // The reader is flushed manually + reader := metric.NewPeriodicReader(metExp) + // The flusher is called after every invocation. We only need to flush + // metrics here because traces are sent synchronously. + flusher := &lambdaFlusher{ + Reader: reader, + } + mp := metric.NewMeterProvider( + metric.WithReader(reader), + ) + otel.SetMeterProvider(mp) + if exprtr, err := otlptracegrpc.New(ctx); err != nil { + return nil, err + } else { + // Use WithSyncer to flush all spans each invocation + tpOpts = append(tpOpts, sdktrace.WithSyncer(exprtr)) + } + registry, err := metrics.NewOtelRegistry(mp) + if err != nil { + return nil, err + } + proc := processor.NewInboundMetricsSpanProcessor(registry) + prop := propagation.NewCompositeTextMapPropagator( + &propagation.TraceContext{}, + &propagation.Baggage{}, + &propagator.SolarwindsPropagator{}, + ) + smplr, err := sampler.NewSampler(o) + if err != nil { + return nil, err + } + otel.SetTextMapPropagator(prop) + // Default resource detection plus our required attributes + var resrc *resource.Resource + resrc, err = resource.Merge( + resource.Default(), + resource.NewSchemaless( + attribute.String("sw.data.module", "apm"), + attribute.String("sw.apm.version", utils.Version()), + attribute.String("faas.instance", lambdaLogStreamName), + ), + ) + if err != nil { + return nil, err + } + + tpOpts = append(tpOpts, + sdktrace.WithResource(resrc), + sdktrace.WithSampler(smplr), + sdktrace.WithSpanProcessor(proc), + ) + otel.SetTracerProvider(sdktrace.NewTracerProvider(tpOpts...)) + return flusher, nil +} From 412ba038707af2d0a97b9faa10e17b12d46189e0 Mon Sep 17 00:00:00 2001 From: Jared Harper <129781402+swi-jared@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:55:49 -0700 Subject: [PATCH 30/39] [NH-78293] Sample rate metrics for Lambda (#105) --- internal/metrics/metrics.go | 46 ++----- internal/metrics/metrics_test.go | 20 ++- internal/metrics/registry.go | 4 +- internal/oboe/file_watcher.go | 9 +- internal/oboe/oboe.go | 163 +++++++++++++----------- internal/oboe/settings.go | 51 ++------ internal/oboe/settings_lambda.go | 6 - internal/oboe/settings_lambda_test.go | 11 +- internal/oboetestutils/oboe.go | 40 ++---- internal/processor/processor_test.go | 2 +- internal/reporter/reporter_grpc.go | 2 +- internal/reporter/reporter_grpc_test.go | 1 - swo/agent.go | 3 + 13 files changed, 155 insertions(+), 203 deletions(-) diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index b46b4d4a..88d6714c 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -65,13 +65,6 @@ const ( TriggeredTraceCount = "TriggeredTraceCount" ) -// Request counters collection categories -const ( - RCRegular = "ReqCounterRegular" - RCRelaxedTriggerTrace = "ReqCounterRelaxedTriggerTrace" - RCStrictTriggerTrace = "ReqCounterStrictTriggerTrace" -) - // metric names const ( transactionResponseTime = "TransactionResponseTime" @@ -186,6 +179,11 @@ func (s *EventQueueStats) TotalEventsAdd(n int64) { // RateCounts is the rate counts reported by trace sampler type RateCounts struct{ requested, sampled, limited, traced, through int64 } +// RateCountSummary is used to merge RateCounts from multiple token buckets +type RateCountSummary struct { + Requested, Traced, Limited, TtTraced, Sampled, Through int64 +} + // FlushRateCounts reset the counters and returns the current value func (c *RateCounts) FlushRateCounts() *RateCounts { return &RateCounts{ @@ -238,32 +236,16 @@ func (c *RateCounts) Through() int64 { } // addRequestCounters add various request-related counters to the metrics message buffer. -func addRequestCounters(bbuf *bson.Buffer, index *int, rcs map[string]*RateCounts) { - var requested, traced, limited, ttTraced int64 - - for _, rc := range rcs { - requested += rc.Requested() - traced += rc.Traced() - limited += rc.Limited() - } - - addMetricsValue(bbuf, index, RequestCount, requested) - addMetricsValue(bbuf, index, TraceCount, traced) - addMetricsValue(bbuf, index, TokenBucketExhaustionCount, limited) - - if rcRegular, ok := rcs[RCRegular]; ok { - addMetricsValue(bbuf, index, SampleCount, rcRegular.Sampled()) - addMetricsValue(bbuf, index, ThroughTraceCount, rcRegular.Through()) - } - - if relaxed, ok := rcs[RCRelaxedTriggerTrace]; ok { - ttTraced += relaxed.Traced() - } - if strict, ok := rcs[RCStrictTriggerTrace]; ok { - ttTraced += strict.Traced() +func addRequestCounters(bbuf *bson.Buffer, index *int, rcs *RateCountSummary) { + if rcs == nil { + return } - - addMetricsValue(bbuf, index, TriggeredTraceCount, ttTraced) + addMetricsValue(bbuf, index, RequestCount, rcs.Requested) + addMetricsValue(bbuf, index, TraceCount, rcs.Traced) + addMetricsValue(bbuf, index, TokenBucketExhaustionCount, rcs.Limited) + addMetricsValue(bbuf, index, SampleCount, rcs.Sampled) + addMetricsValue(bbuf, index, ThroughTraceCount, rcs.Through) + addMetricsValue(bbuf, index, TriggeredTraceCount, rcs.TtTraced) } // SetCap sets the maximum number of distinct metrics allowed. diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index e3e0d15a..dcdff5e8 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -384,10 +384,14 @@ func TestGenerateMetricsMessage(t *testing.T) { reg := NewLegacyRegistry(false).(*registry) flushInterval := int32(60) bbuf := bson.WithBuf(reg.BuildBuiltinMetricsMessage(flushInterval, &EventQueueStats{}, - map[string]*RateCounts{ // requested, sampled, limited, traced, through - RCRegular: {10, 2, 5, 5, 1}, - RCRelaxedTriggerTrace: {3, 0, 1, 2, 0}, - RCStrictTriggerTrace: {4, 0, 3, 1, 0}}, true)) + &RateCountSummary{ + Requested: 10, + Sampled: 2, + Limited: 5, + Traced: 5, + Through: 1, + TtTraced: 3, + }, true)) m, err := bsonToMap(bbuf) require.NoError(t, err) @@ -407,8 +411,6 @@ func TestGenerateMetricsMessage(t *testing.T) { value interface{} } - t.Logf("Got metrics: %+v", mts) - testCases := []testCase{ {"RequestCount", int64(10)}, {"TraceCount", int64(5)}, @@ -463,6 +465,10 @@ func TestGenerateMetricsMessage(t *testing.T) { for i, tc := range testCases { assert.Equal(t, tc.name, mts[i].(map[string]interface{})["name"]) assert.IsType(t, mts[i].(map[string]interface{})["value"], tc.value, tc.name) + // test the values of the sample rate metrics + if i < 6 { + assert.Equal(t, tc.value, mts[i].(map[string]interface{})["value"], tc.name) + } } assert.Nil(t, m["TransactionNameOverflow"]) @@ -475,7 +481,7 @@ func TestGenerateMetricsMessage(t *testing.T) { } m, err = bsonToMap(bson.WithBuf(reg.BuildBuiltinMetricsMessage(flushInterval, &EventQueueStats{}, - map[string]*RateCounts{RCRegular: {}, RCRelaxedTriggerTrace: {}, RCStrictTriggerTrace: {}}, true))) + &RateCountSummary{}, true))) require.NoError(t, err) assert.NotNil(t, m["TransactionNameOverflow"]) diff --git a/internal/metrics/registry.go b/internal/metrics/registry.go index f1fa5c70..d60c8a78 100644 --- a/internal/metrics/registry.go +++ b/internal/metrics/registry.go @@ -55,7 +55,7 @@ type MetricRegistry interface { type LegacyRegistry interface { MetricRegistry BuildBuiltinMetricsMessage(flushInterval int32, qs *EventQueueStats, - rcs map[string]*RateCounts, runtimeMetrics bool) []byte + rcs *RateCountSummary, runtimeMetrics bool) []byte BuildCustomMetricsMessage(flushInterval int32) []byte ApmMetricsCap() int32 SetApmMetricsCap(int32) @@ -97,7 +97,7 @@ func (r *registry) BuildCustomMetricsMessage(flushInterval int32) []byte { // // return metrics message in BSON format func (r *registry) BuildBuiltinMetricsMessage(flushInterval int32, qs *EventQueueStats, - rcs map[string]*RateCounts, runtimeMetrics bool) []byte { + rcs *RateCountSummary, runtimeMetrics bool) []byte { var m = r.apmMetrics.CopyAndReset(flushInterval) if m == nil { return nil diff --git a/internal/oboe/file_watcher.go b/internal/oboe/file_watcher.go index 8184d298..6d78af76 100644 --- a/internal/oboe/file_watcher.go +++ b/internal/oboe/file_watcher.go @@ -62,14 +62,7 @@ func (w *fileBasedWatcher) readSettingFromFile() { "Got lambda settings from file:\n%+v", s, ) - w.o.UpdateSetting( - int32(s.sType), - s.layer, - s.flags, - s.value, - s.ttl, - s.args, - ) + w.o.UpdateSetting(s.flags, s.value, time.Second*time.Duration(s.ttl), s.args) } // Start runs a ticker that checks settings expiry from cache diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 10c83213..81ece88f 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -15,12 +15,14 @@ package oboe import ( + "context" "encoding/binary" "errors" "fmt" + "go.opentelemetry.io/otel/metric" "math" "strings" - "sync" + "sync/atomic" "time" "github.com/solarwinds/apm-go/internal/config" @@ -47,47 +49,100 @@ const ( ) type Oboe interface { - UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) + UpdateSetting(flags []byte, value int64, ttl time.Duration, args map[string][]byte) CheckSettingsTimeout() - GetSetting() (*settings, bool) + GetSetting() *settings RemoveSetting() HasDefaultSetting() bool SampleRequest(continued bool, url string, triggerTrace TriggerTraceMode, swState w3cfmt.SwTraceState) SampleDecision - FlushRateCounts() map[string]*metrics.RateCounts + FlushRateCounts() *metrics.RateCountSummary GetTriggerTraceToken() ([]byte, error) + RegisterOtelSampleRateMetrics(mp metric.MeterProvider) error } func NewOboe() Oboe { - return &oboe{ - settings: make(map[settingKey]*settings), - } + return &oboe{} } type oboe struct { - sync.RWMutex - settings map[settingKey]*settings + settings atomic.Pointer[settings] } var _ Oboe = &oboe{} +func (o *oboe) RegisterOtelSampleRateMetrics(mp metric.MeterProvider) error { + meter := mp.Meter("sw.apm.sampling.metrics") + traceCount, err := meter.Int64ObservableGauge("trace.service.tracecount") + if err != nil { + return err + } + sampleCount, err := meter.Int64ObservableGauge("trace.service.samplecount") + if err != nil { + return err + } + requestCount, err := meter.Int64ObservableGauge("trace.service.request_count") + if err != nil { + return err + } + tokenBucketExhaustionCount, err := meter.Int64ObservableGauge("trace.service.tokenbucket_exhaustion_count") + if err != nil { + return err + } + throughTraceCount, err := meter.Int64ObservableGauge("trace.service.through_trace_count") + if err != nil { + return err + } + triggeredTraceCount, err := meter.Int64ObservableGauge("trace.service.triggered_trace_count") + if err != nil { + return err + } + + _, err = meter.RegisterCallback( + func(_ context.Context, obs metric.Observer) error { + if rateCounts := o.FlushRateCounts(); rateCounts != nil { + obs.ObserveInt64(traceCount, rateCounts.Traced) + obs.ObserveInt64(sampleCount, rateCounts.Sampled) + obs.ObserveInt64(requestCount, rateCounts.Requested) + obs.ObserveInt64(tokenBucketExhaustionCount, rateCounts.Limited) + obs.ObserveInt64(throughTraceCount, rateCounts.Through) + obs.ObserveInt64(triggeredTraceCount, rateCounts.TtTraced) + } + return nil + }, + traceCount, + sampleCount, + requestCount, + tokenBucketExhaustionCount, + throughTraceCount, + triggeredTraceCount, + ) + return err +} + // FlushRateCounts collects the request counters values by categories. -func (o *oboe) FlushRateCounts() map[string]*metrics.RateCounts { - setting, ok := o.GetSetting() - if !ok { +func (o *oboe) FlushRateCounts() *metrics.RateCountSummary { + s := o.GetSetting() + if s == nil { return nil } - rcs := make(map[string]*metrics.RateCounts) - rcs[metrics.RCRegular] = setting.bucket.FlushRateCounts() - rcs[metrics.RCRelaxedTriggerTrace] = setting.triggerTraceRelaxedBucket.FlushRateCounts() - rcs[metrics.RCStrictTriggerTrace] = setting.triggerTraceStrictBucket.FlushRateCounts() - - return rcs + regular := s.bucket.FlushRateCounts() + relaxedTT := s.triggerTraceRelaxedBucket.FlushRateCounts() + strictTT := s.triggerTraceStrictBucket.FlushRateCounts() + + return &metrics.RateCountSummary{ + Sampled: regular.Sampled(), + Through: regular.Through(), + Requested: regular.Requested() + relaxedTT.Requested() + strictTT.Requested(), + Traced: regular.Traced() + relaxedTT.Traced() + strictTT.Traced(), + Limited: regular.Limited() + relaxedTT.Limited() + strictTT.Limited(), + TtTraced: relaxedTT.Traced() + strictTT.Traced(), + } } // SampleRequest returns a SampleDecision based on inputs and state of various token buckets func (o *oboe) SampleRequest(continued bool, url string, triggerTrace TriggerTraceMode, swState w3cfmt.SwTraceState) SampleDecision { - setting, ok := o.GetSetting() - if !ok { + setting := o.GetSetting() + if setting == nil { return SampleDecision{false, 0, SampleSourceNone, false, TtSettingsNotAvailable, 0, 0, false} } @@ -213,16 +268,15 @@ func adjustSampleRate(rate int64) int { return int(rate) } -func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) { +func (o *oboe) UpdateSetting(flags []byte, value int64, ttl time.Duration, args map[string][]byte) { ns := newOboeSettings() ns.timestamp = time.Now() - ns.source = settingType(sType).toSampleSource() + ns.source = SampleSourceDefault ns.flags = flagStringToBin(string(flags)) ns.originalFlags = ns.flags ns.value = adjustSampleRate(value) ns.ttl = ttl - ns.layer = layer ns.TriggerToken = args[constants.KvSignatureKey] @@ -238,16 +292,9 @@ func (o *oboe) UpdateSetting(sType int32, layer string, flags []byte, value int6 tStrictCapacity := parseFloat64(args, constants.KvTriggerTraceStrictBucketCapacity, 0) ns.triggerTraceStrictBucket.setRateCap(tStrictRate, tStrictCapacity) - merged := mergeLocalSetting(ns) + ns.MergeLocalSetting() - key := settingKey{ - sType: settingType(sType), - layer: layer, - } - - o.Lock() - o.settings[key] = merged - o.Unlock() + o.settings.Store(ns) } // CheckSettingsTimeout checks and deletes expired settings @@ -256,57 +303,31 @@ func (o *oboe) CheckSettingsTimeout() { } func (o *oboe) checkSettingsTimeout() { - o.Lock() - defer o.Unlock() - - ss := o.settings - for k, s := range ss { - e := s.timestamp.Add(time.Duration(s.ttl) * time.Second) - if e.Before(time.Now()) { - delete(ss, k) - } - } -} - -func (o *oboe) GetSetting() (*settings, bool) { - o.RLock() - defer o.RUnlock() - - // always use the default setting - key := settingKey{ - sType: TypeDefault, - layer: "", + s := o.settings.Load() + if s == nil { + return } - if setting, ok := o.settings[key]; ok { - return setting, true + e := s.timestamp.Add(time.Duration(s.ttl) * time.Second) + if e.Before(time.Now()) { + o.settings.Store(nil) } +} - return nil, false +func (o *oboe) GetSetting() *settings { + return o.settings.Load() } func (o *oboe) RemoveSetting() { - o.Lock() - defer o.Unlock() - - // always use the default setting - key := settingKey{ - sType: TypeDefault, - layer: "", - } - - delete(o.settings, key) + o.settings.Store(nil) } func (o *oboe) HasDefaultSetting() bool { - if _, ok := o.GetSetting(); ok { - return true - } - return false + return o.settings.Load() != nil } func (o *oboe) GetTriggerTraceToken() ([]byte, error) { - setting, ok := o.GetSetting() - if !ok { + setting := o.GetSetting() + if setting == nil { return nil, errors.New("failed to get settings") } if len(setting.TriggerToken) == 0 { diff --git a/internal/oboe/settings.go b/internal/oboe/settings.go index 9d0bfe29..22a81b0b 100644 --- a/internal/oboe/settings.go +++ b/internal/oboe/settings.go @@ -32,8 +32,7 @@ type settings struct { value int // The sample source after negotiating with local config source SampleSource - ttl int64 - layer string + ttl time.Duration TriggerToken []byte bucket *tokenBucket triggerTraceRelaxedBucket *tokenBucket @@ -43,6 +42,7 @@ type settings struct { func (s *settings) hasOverrideFlag() bool { return s.originalFlags&FlagOverride != 0 } + func newOboeSettings() *settings { return &settings{ // The global token bucket. Trace decisions of all the requests are controlled @@ -58,29 +58,26 @@ func newOboeSettings() *settings { } } -// mergeLocalSetting follow the predefined precedence to decide which one to +// MergeLocalSetting follow the predefined precedence to decide which one to // pick from: either the local configs or the remote ones, or the combination. -// -// Note: This function modifies the argument in place. -func mergeLocalSetting(remote *settings) *settings { - if remote.hasOverrideFlag() && config.SamplingConfigured() { +func (s *settings) MergeLocalSetting() { + if s.hasOverrideFlag() && config.SamplingConfigured() { // Choose the lower sample rate and merge the flags - if remote.value > config.GetSampleRate() { - remote.value = config.GetSampleRate() - remote.source = SampleSourceFile + if s.value > config.GetSampleRate() { + s.value = config.GetSampleRate() + s.source = SampleSourceFile } - remote.flags &= NewTracingMode(config.GetTracingMode()).toFlags() + s.flags &= NewTracingMode(config.GetTracingMode()).toFlags() } else if config.SamplingConfigured() { // Use local sample rate and tracing mode config - remote.value = config.GetSampleRate() - remote.flags = NewTracingMode(config.GetTracingMode()).toFlags() - remote.source = SampleSourceFile + s.value = config.GetSampleRate() + s.flags = NewTracingMode(config.GetTracingMode()).toFlags() + s.source = SampleSourceFile } if !config.GetTriggerTrace() { - remote.flags = remote.flags &^ (1 << FlagTriggerTraceOffset) + s.flags = s.flags &^ (1 << FlagTriggerTraceOffset) } - return remote } // mergeURLSetting merges the service level setting (merged from remote and local @@ -123,19 +120,8 @@ func (s *settings) getTokenBucketSetting(ttMode TriggerTraceMode) (capacity floa return bucket.capacity, bucket.ratePerSec } -// The identifying keys for a setting -type settingKey struct { - sType settingType - layer string -} -type settingType int type settingFlag uint16 -// setting types -const ( - TypeDefault settingType = iota // default setting and the only accepted setting -) - // setting flags offset const ( FlagInvalidOffset = iota @@ -168,14 +154,3 @@ func (f settingFlag) Enabled() bool { func (f settingFlag) TriggerTraceEnabled() bool { return f&FlagTriggerTrace != 0 } - -func (st settingType) toSampleSource() SampleSource { - var source SampleSource - switch st { - case TypeDefault: - source = SampleSourceDefault - default: - source = SampleSourceNone - } - return source -} diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 365ffa58..50c664cb 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -26,10 +26,8 @@ import ( type settingLambdaFromFile struct { Arguments *settingArguments `json:"arguments"` Flags string `json:"flags"` - Layer string `json:"layer"` Timestamp int64 `json:"timestamp"` Ttl int64 `json:"ttl"` - Stype int `json:"type"` Value int64 `json:"value"` } @@ -44,8 +42,6 @@ type settingArguments struct { } type settingLambdaNormalized struct { - sType settingType - layer string flags []byte value int64 ttl int64 @@ -71,8 +67,6 @@ func newSettingLambdaNormalized(fromFile *settingLambdaFromFile) *settingLambdaN ) settingNorm := &settingLambdaNormalized{ - TypeDefault, // always DEFAULT_SAMPLE_RATE - "", // not set since type is always DEFAULT_SAMPLE_RATE flags, fromFile.Value, fromFile.Ttl, diff --git a/internal/oboe/settings_lambda_test.go b/internal/oboe/settings_lambda_test.go index cf41686a..a495c371 100644 --- a/internal/oboe/settings_lambda_test.go +++ b/internal/oboe/settings_lambda_test.go @@ -15,12 +15,11 @@ package oboe import ( - "os" - "testing" - "github.com/solarwinds/apm-go/internal/constants" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "os" + "testing" ) func TestNewSettingLambdaNormalized(t *testing.T) { @@ -36,16 +35,12 @@ func TestNewSettingLambdaNormalized(t *testing.T) { fromFile := settingLambdaFromFile{ &settingArgs, "SAMPLE_START,SAMPLE_THROUGH_ALWAYS,SAMPLE_BUCKET_ENABLED,TRIGGER_TRACE", - "", 1715900164, 120, - 0, 1000000, } result := newSettingLambdaNormalized(&fromFile) - assert.Equal(t, TypeDefault, result.sType) - assert.Equal(t, "", result.layer) assert.Equal( t, []byte{0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x55, 0x47, 0x48, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x2c, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45}, @@ -138,8 +133,6 @@ func TestNewSettingLambdaFromFile(t *testing.T) { require.NoError(t, os.WriteFile(settingsFileName, content, 0644)) result, err := newSettingLambdaFromFile() assert.Nil(t, err) - assert.Equal(t, TypeDefault, result.sType) - assert.Equal(t, "", result.layer) assert.Equal( t, []byte{0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x55, 0x47, 0x48, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x2c, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x2c, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45}, diff --git a/internal/oboetestutils/oboe.go b/internal/oboetestutils/oboe.go index 6fb293fe..d317b7b4 100644 --- a/internal/oboetestutils/oboe.go +++ b/internal/oboetestutils/oboe.go @@ -14,61 +14,47 @@ package oboetestutils -import "github.com/solarwinds/apm-go/internal/utils" +import ( + "github.com/solarwinds/apm-go/internal/utils" + "time" +) const TestToken = "TOKEN" -const TypeDefault = 0 type SettingUpdater interface { - UpdateSetting(sType int32, layer string, flags []byte, value int64, ttl int64, args map[string][]byte) + UpdateSetting(flags []byte, value int64, ttl time.Duration, args map[string][]byte) } func AddDefaultSetting(o SettingUpdater) { // add default setting with 100% sampling - o.UpdateSetting(int32(TypeDefault), "", - []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), - 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddSampleThrough(o SettingUpdater) { // add default setting with 100% sampling - o.UpdateSetting(int32(TypeDefault), "", - []byte("SAMPLE_START,SAMPLE_THROUGH,TRIGGER_TRACE"), - 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("SAMPLE_START,SAMPLE_THROUGH,TRIGGER_TRACE"), 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddNoTriggerTrace(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS"), - 1000000, 120, utils.ArgsToMap(1000000, 1000000, 0, 0, 0, 0, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS"), 1000000, 120, utils.ArgsToMap(1000000, 1000000, 0, 0, 0, 0, -1, -1, []byte(TestToken))) } func AddTriggerTraceOnly(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte("TRIGGER_TRACE"), - 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("TRIGGER_TRACE"), 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddRelaxedTriggerTraceOnly(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte("TRIGGER_TRACE"), - 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 0, 0, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("TRIGGER_TRACE"), 0, 120, utils.ArgsToMap(0, 0, 1000000, 1000000, 0, 0, -1, -1, []byte(TestToken))) } func AddStrictTriggerTraceOnly(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte("TRIGGER_TRACE"), - 0, 120, utils.ArgsToMap(0, 0, 0, 0, 1000000, 1000000, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("TRIGGER_TRACE"), 0, 120, utils.ArgsToMap(0, 0, 0, 0, 1000000, 1000000, -1, -1, []byte(TestToken))) } func AddLimitedTriggerTrace(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), - 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1, 1, 1, 1, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte("SAMPLE_START,SAMPLE_THROUGH_ALWAYS,TRIGGER_TRACE"), 1000000, 120, utils.ArgsToMap(1000000, 1000000, 1, 1, 1, 1, -1, -1, []byte(TestToken))) } func AddDisabled(o SettingUpdater) { - o.UpdateSetting(int32(TypeDefault), "", - []byte(""), - 0, 120, utils.ArgsToMap(0, 0, 1, 1, 1, 1, -1, -1, []byte(TestToken))) + o.UpdateSetting([]byte(""), 0, 120, utils.ArgsToMap(0, 0, 1, 1, 1, 1, -1, -1, []byte(TestToken))) } diff --git a/internal/processor/processor_test.go b/internal/processor/processor_test.go index 704e33b0..c765af3f 100644 --- a/internal/processor/processor_test.go +++ b/internal/processor/processor_test.go @@ -35,7 +35,7 @@ func (r *recordMock) RecordSpan(span sdktrace.ReadOnlySpan) { r.called = true } -func (r *recordMock) BuildBuiltinMetricsMessage(int32, *metrics.EventQueueStats, map[string]*metrics.RateCounts, bool) []byte { +func (r *recordMock) BuildBuiltinMetricsMessage(int32, *metrics.EventQueueStats, *metrics.RateCountSummary, bool) []byte { panic("should not be called in this test") } diff --git a/internal/reporter/reporter_grpc.go b/internal/reporter/reporter_grpc.go index af102ea1..56dfea76 100644 --- a/internal/reporter/reporter_grpc.go +++ b/internal/reporter/reporter_grpc.go @@ -890,7 +890,7 @@ func (r *grpcReporter) getSettings() (*collector.SettingsResult, error) { // settings new settings func (r *grpcReporter) updateSettings(settings *collector.SettingsResult) { for _, s := range settings.GetSettings() { - r.oboe.UpdateSetting(int32(s.Type), string(s.Layer), s.Flags, s.Value, s.Ttl, s.Arguments) + r.oboe.UpdateSetting(s.Flags, s.Value, time.Duration(s.Ttl)*time.Second, s.Arguments) // update MetricsFlushInterval mi := ParseInt32(s.Arguments, constants.KvMetricsFlushInterval, r.collectMetricInterval) diff --git a/internal/reporter/reporter_grpc_test.go b/internal/reporter/reporter_grpc_test.go index e9809363..25d583d2 100644 --- a/internal/reporter/reporter_grpc_test.go +++ b/internal/reporter/reporter_grpc_test.go @@ -135,7 +135,6 @@ func (s *TestGRPCServer) GetSettings(ctx context.Context, req *pb.SettingsReques Settings: []*pb.OboeSetting{{ Type: pb.OboeSettingType_DEFAULT_SAMPLE_RATE, // Flags: XXX, - // Layer: "", // default, specifically not setting layer/service // Timestamp: XXX, Value: 1000000, Arguments: map[string][]byte{ diff --git a/swo/agent.go b/swo/agent.go index d9298ffa..9e863132 100644 --- a/swo/agent.go +++ b/swo/agent.go @@ -190,6 +190,9 @@ func StartLambda(lambdaLogStreamName string) (Flusher, error) { metric.WithReader(reader), ) otel.SetMeterProvider(mp) + if err = o.RegisterOtelSampleRateMetrics(mp); err != nil { + return nil, err + } if exprtr, err := otlptracegrpc.New(ctx); err != nil { return nil, err } else { From 8eb7cb0310f12416e414d80d133e9dbf28b7cc6d Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Fri, 28 Jun 2024 14:16:26 -0700 Subject: [PATCH 31/39] go mod tidy --- go.sum | 75 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/go.sum b/go.sum index af644881..4e1ae8a8 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.6 h1:vVOuhRyslJ6T/HteG71ZWCT github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.6/go.mod h1:jimWaqLiT0sJGLh51dKCLLtExRYPtMU7MpxuCgtbkxg= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.1.2/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= @@ -14,50 +16,73 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/solarwinds/apm-proto v1.0.5 h1:HH1bozLsH+j1Nqn/MKb+h3aNEmrI8OcCWZJFbVEvjr8= github.com/solarwinds/apm-proto v1.0.5/go.mod h1:CN4fCYBnxyOJlBV0CYNXLz6lzNH8SCfNqcCBbpai76c= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= From 4aebb48f475d33b44b8c3e45b53929ba06806316 Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 09:23:53 -0700 Subject: [PATCH 32/39] add debug logs in checkSettingsTimeout --- internal/oboe/oboe.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 81ece88f..2c8378c0 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -305,10 +305,13 @@ func (o *oboe) CheckSettingsTimeout() { func (o *oboe) checkSettingsTimeout() { s := o.settings.Load() if s == nil { + log.Debug("checkSettingsTimeout: No settings") return } e := s.timestamp.Add(time.Duration(s.ttl) * time.Second) + log.Debugf("checkSettingsTimeout: ttl: %d, timestamp: %s, boundary: %s", s.ttl, s.timestamp, e) if e.Before(time.Now()) { + log.Debugf("checkSettingsTimeout: ttl exceeded, expiring settings") o.settings.Store(nil) } } From 5346f556552f7e9a8e3ca910d8628e07a8b76cdd Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 10:07:52 -0700 Subject: [PATCH 33/39] Don't adjust a time.Duration twice --- internal/oboe/oboe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 2c8378c0..02c5bcd6 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -308,7 +308,7 @@ func (o *oboe) checkSettingsTimeout() { log.Debug("checkSettingsTimeout: No settings") return } - e := s.timestamp.Add(time.Duration(s.ttl) * time.Second) + e := s.timestamp.Add(s.ttl) log.Debugf("checkSettingsTimeout: ttl: %d, timestamp: %s, boundary: %s", s.ttl, s.timestamp, e) if e.Before(time.Now()) { log.Debugf("checkSettingsTimeout: ttl exceeded, expiring settings") From 5dba2b5ab523b541202af9d513d308f103ee52de Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 10:25:41 -0700 Subject: [PATCH 34/39] str format --- internal/oboe/oboe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/oboe/oboe.go b/internal/oboe/oboe.go index 02c5bcd6..6a5849c4 100644 --- a/internal/oboe/oboe.go +++ b/internal/oboe/oboe.go @@ -309,7 +309,7 @@ func (o *oboe) checkSettingsTimeout() { return } e := s.timestamp.Add(s.ttl) - log.Debugf("checkSettingsTimeout: ttl: %d, timestamp: %s, boundary: %s", s.ttl, s.timestamp, e) + log.Debugf("checkSettingsTimeout: ttl: %s, timestamp: %s, boundary: %s", s.ttl, s.timestamp, e) if e.Before(time.Now()) { log.Debugf("checkSettingsTimeout: ttl exceeded, expiring settings") o.settings.Store(nil) From df8273f912fb02636595d7e2331cbda832de804f Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 11:01:33 -0700 Subject: [PATCH 35/39] Fix gomod version --- instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod index 88f1dfdf..b787e72c 100644 --- a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod @@ -14,7 +14,7 @@ module github.com/solarwinds/apm-go/instrumentation/github.com/aws/aws-lambda-go/swolambda -go 1.22.3 +go 1.21 require ( github.com/aws/aws-lambda-go v1.47.0 From bdd8033973807beb8e2313cf353431f8a500c209 Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 13:01:37 -0700 Subject: [PATCH 36/39] update otel deps --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index e43f599b..609aec08 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 - go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/metric v1.28.0 go.opentelemetry.io/otel/sdk/metric v1.27.0 go.uber.org/atomic v1.11.0 google.golang.org/grpc v1.64.0 @@ -56,9 +56,9 @@ require ( require ( github.com/stretchr/objx v0.5.2 // indirect - go.opentelemetry.io/otel v1.27.0 - go.opentelemetry.io/otel/sdk v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 - golang.org/x/sys v0.20.0 // indirect + go.opentelemetry.io/otel v1.28.0 + go.opentelemetry.io/otel/sdk v1.28.0 + go.opentelemetry.io/otel/trace v1.28.0 + golang.org/x/sys v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4e1ae8a8..238fc5a5 100644 --- a/go.sum +++ b/go.sum @@ -44,22 +44,22 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -68,8 +68,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= From e89a6703a324df36db1a59f2d69a7acbe3bc4839 Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Tue, 9 Jul 2024 13:02:43 -0700 Subject: [PATCH 37/39] update go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 609aec08..2d1c4826 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/solarwinds/apm-proto v1.0.5 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 go.opentelemetry.io/otel/metric v1.28.0 diff --git a/go.sum b/go.sum index 238fc5a5..4a77f906 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= From 4c8bc8a88290cd73f26f45ab5cc0e305e3b2ebe1 Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Fri, 12 Jul 2024 11:03:40 -0700 Subject: [PATCH 38/39] Add comment regarding settingLambdaNormalized --- internal/oboe/settings_lambda.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/oboe/settings_lambda.go b/internal/oboe/settings_lambda.go index 50c664cb..a2f972d2 100644 --- a/internal/oboe/settings_lambda.go +++ b/internal/oboe/settings_lambda.go @@ -41,6 +41,10 @@ type settingArguments struct { TriggerStrictBucketRate float64 `json:"TriggerStrictBucketRate"` } +// N.B. this struct adheres to the types required by the oboe.Oboe interface. In the future, +// we should make the interface smarter about the incoming types so we're not converting from +// known types to []byte and back. A task has been created to track this work, though it also +// might make sense to do it when/if the GetSettings call is refactored. type settingLambdaNormalized struct { flags []byte value int64 From 8efbb189a001dd000b9f309908c9db2a8040ff14 Mon Sep 17 00:00:00 2001 From: Jared Harper Date: Mon, 22 Jul 2024 11:48:34 -0700 Subject: [PATCH 39/39] set depdendency version to v1.1.0 --- instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod index b787e72c..392b7cd0 100644 --- a/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod +++ b/instrumentation/github.com/aws/aws-lambda-go/swolambda/go.mod @@ -18,8 +18,7 @@ go 1.21 require ( github.com/aws/aws-lambda-go v1.47.0 - // TODO this has to be updated to a version that actually has support for Lambda - github.com/solarwinds/apm-go v1.0.0 + github.com/solarwinds/apm-go v1.1.0 go.opentelemetry.io/otel v1.27.0 )