Skip to content

Commit

Permalink
Support Environment Variable for Jaeger Remote Sampler (#6310)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
Resolves #6256 

## Description of the changes
- Added capability to configure jaeger remote sampler using
`OTEL_TRACES_SAMPLER_ARG`
- Environment variables would be applied before the code values for the
corresponding keys.
- If the corresponding values exist in code, we take those.

---------

Signed-off-by: Alok Kumar Singh <dev.alok.singh123@gmail.com>
Co-authored-by: Damien Mathieu <42@dmathieu.com>
  • Loading branch information
akstron and dmathieu authored Nov 15, 2024
1 parent a7ba1f9 commit c18691f
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Added support for providing `endpoint`, `pollingIntervalMs` and `initialSamplingRate` using environment variable `OTEL_TRACES_SAMPLER_ARG` in `go.opentelemetry.io/contrib/samples/jaegerremote`. (#6310)

<!-- Released section -->
<!-- Don't change this section unless doing release -->

Expand Down
54 changes: 54 additions & 0 deletions samplers/jaegerremote/sampler_remote_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
package jaegerremote // import "go.opentelemetry.io/contrib/samplers/jaegerremote"

import (
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
Expand All @@ -37,6 +41,45 @@ type config struct {
logger logr.Logger
}

func getEnvOptions() ([]Option, []error) {
var options []Option
// list of errors which will be logged once logger is set by the user
var errs []error

args := strings.Split(os.Getenv("OTEL_TRACES_SAMPLER_ARG"), ",")
for _, arg := range args {
keyValue := strings.Split(arg, "=")
if len(keyValue) != 2 {
errs = append(errs, fmt.Errorf("argument %s is not of type '<key>=<value>'", arg))
continue
}
key := strings.Trim(keyValue[0], " ")
value := strings.Trim(keyValue[1], " ")

switch key {
case "endpoint":
options = append(options, WithSamplingServerURL(value))
case "pollingIntervalMs":
intervalMs, err := strconv.Atoi(value)
if err != nil {
errs = append(errs, fmt.Errorf("%s parsing failed with :%w", key, err))
continue
}
options = append(options, WithSamplingRefreshInterval(time.Duration(intervalMs)*time.Millisecond))
case "initialSamplingRate":
samplingRate, err := strconv.ParseFloat(value, 64)
if err != nil {
errs = append(errs, fmt.Errorf("%s parsing failed with :%w", key, err))
continue
}
options = append(options, WithInitialSampler(trace.TraceIDRatioBased(samplingRate)))
default:
errs = append(errs, fmt.Errorf("invalid argument %s in OTEL_TRACE_SAMPLER_ARG", key))
}
}
return options, errs
}

// newConfig returns an appropriately configured config.
func newConfig(options ...Option) config {
c := config{
Expand All @@ -55,9 +98,20 @@ func newConfig(options ...Option) config {
},
logger: logr.Discard(),
}

envOptions, errs := getEnvOptions()
for _, option := range envOptions {
option.apply(&c)
}

for _, option := range options {
option.apply(&c)
}

for _, err := range errs {
c.logger.Error(err, "env variable parsing failure")
}

c.updaters = append([]samplerUpdater{&perOperationSamplerUpdater{
MaxOperations: c.posParams.MaxOperations,
OperationNameLateBinding: c.posParams.OperationNameLateBinding,
Expand Down
59 changes: 59 additions & 0 deletions samplers/jaegerremote/sampler_remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,62 @@ func TestDefaultSamplingStrategyFetcher_Timeout(t *testing.T) {
fetcher := newHTTPSamplingStrategyFetcher("")
assert.Equal(t, defaultRemoteSamplingTimeout, fetcher.httpClient.Timeout)
}

func TestEnvVarSettingForNewTracer(t *testing.T) {
type testConfig struct {
samplingServerURL string
samplingRefreshInterval time.Duration
}

tests := []struct {
otelTraceSamplerArgs string
expErrs []string
codeOptions []Option
expConfig testConfig
}{
{
otelTraceSamplerArgs: "endpoint=http://localhost:14250,pollingIntervalMs=5000,initialSamplingRate=0.25",
expErrs: []string{},
},
{
otelTraceSamplerArgs: "endpointhttp://localhost:14250,pollingIntervalMs=5x000,initialSamplingRate=0.xyz25,invalidKey=invalidValue",
expErrs: []string{
"argument endpointhttp://localhost:14250 is not of type '<key>=<value>'",
"pollingIntervalMs parsing failed",
"initialSamplingRate parsing failed",
"invalid argument invalidKey in OTEL_TRACE_SAMPLER_ARG",
},
},
{
// Make sure we don't override values provided in code
otelTraceSamplerArgs: "endpoint=http://localhost:14250,pollingIntervalMs=5000,initialSamplingRate=0.25",
expErrs: []string{},
codeOptions: []Option{
WithSamplingServerURL("http://localhost:5778"),
},
expConfig: testConfig{
samplingServerURL: "http://localhost:5778",
samplingRefreshInterval: time.Millisecond * 5000,
},
},
}

for _, test := range tests {
t.Run("", func(t *testing.T) {
t.Setenv("OTEL_TRACES_SAMPLER_ARG", test.otelTraceSamplerArgs)

_, errs := getEnvOptions()
require.Equal(t, len(test.expErrs), len(errs))

for i := range len(errs) {
require.ErrorContains(t, errs[i], test.expErrs[i])
}

if test.codeOptions != nil {
cfg := newConfig(test.codeOptions...)
require.Equal(t, test.expConfig.samplingServerURL, cfg.samplingServerURL)
require.Equal(t, test.expConfig.samplingRefreshInterval, cfg.samplingRefreshInterval)
}
})
}
}

0 comments on commit c18691f

Please sign in to comment.