From a700e183d9728bbd9b9e09cc6d20b1083fae717d Mon Sep 17 00:00:00 2001 From: Koenraad Verheyden Date: Sat, 12 Mar 2022 00:01:00 +0100 Subject: [PATCH] Add Jaeger remote sampler (#936) * Add Jaeger remote sampler * add jaeger_remote/example * Extract samplingStrategyParser * Generate code from jaeger-idl * Add per operation sampler, fix CI * Fix Description() * Delete jaeger-idl submodule, directly check in sampling.proto * Update OTel dependencies * Update README.md * Improve test coverage * (Sampler).Description() must not change over time PR to update spec: https://github.com/open-telemetry/opentelemetry-specification/issues/2095 If this is approved we can revert this commit. * Replace internals with jaeger-client-go * Address linting issues * Fix race condition in test * Update samplers/jaegerremote/README.md Co-authored-by: Yuri Shkuro * Update samplers/jaegerremote/internal/testutils/mock_agent.go Co-authored-by: Yuri Shkuro * revmoe go.mod debug info * fix Copyright and * add from * Update dependency * Modified according to pr * change config from https://github.com/open-telemetry/opentelemetry-go/blob/main/CONTRIBUTING.md#configuration * (fix:sampler_remote_options.go):change config * (fix:constants.go):remove Copyright * (fix:samoler_remote.go):fix config and add default * Update samplers/jaegerremote/sampler_remote_options.go yet Co-authored-by: Anthony Mirabella * add local path * fix lint * make precommit fix * (fix:sampler_remote_option.go):The default port of jaeger agent is 5778, but there are other ports specified by the user, so the sampling address and fetch address are strongly bound * change for Style guide * change for Update samplers/jaegerremote/sampler_remote_options.go Co-authored-by: Anthony Mirabella Co-authored-by: Chester Cheung Co-authored-by: Yuri Shkuro Co-authored-by: dino.ma --- .github/dependabot.yml | 18 + samplers/jaegerremote/Makefile | 11 + samplers/jaegerremote/README.md | 38 + samplers/jaegerremote/constants.go | 31 + samplers/jaegerremote/doc.go | 18 + .../jaegerremote/example/docker-compose.yaml | 11 + samplers/jaegerremote/example/go.mod | 13 + samplers/jaegerremote/example/go.sum | 168 ++ samplers/jaegerremote/example/main.go | 55 + .../jaegerremote/example/otel-collector.yaml | 17 + samplers/jaegerremote/example/strategies.json | 30 + samplers/jaegerremote/go.mod | 12 + samplers/jaegerremote/go.sum | 162 ++ .../jaeger-idl/proto/api_v2/sampling.pb.go | 1568 +++++++++++++++++ .../internal/testutils/mock_agent.go | 91 + .../internal/testutils/mock_agent_test.go | 55 + .../internal/testutils/sampling_manager.go | 55 + .../jaegerremote/internal/utils/http_json.go | 59 + .../internal/utils/http_json_test.go | 61 + .../internal/utils/rate_limiter.go | 107 ++ .../internal/utils/rate_limiter_test.go | 105 ++ .../jaeger-idl/proto/api_v2/sampling.proto | 79 + samplers/jaegerremote/sampler.go | 334 ++++ samplers/jaegerremote/sampler_remote.go | 314 ++++ .../jaegerremote/sampler_remote_options.go | 135 ++ samplers/jaegerremote/sampler_remote_test.go | 465 +++++ samplers/jaegerremote/sampler_test.go | 231 +++ 27 files changed, 4243 insertions(+) create mode 100644 samplers/jaegerremote/Makefile create mode 100644 samplers/jaegerremote/README.md create mode 100644 samplers/jaegerremote/constants.go create mode 100644 samplers/jaegerremote/doc.go create mode 100644 samplers/jaegerremote/example/docker-compose.yaml create mode 100644 samplers/jaegerremote/example/go.mod create mode 100644 samplers/jaegerremote/example/go.sum create mode 100644 samplers/jaegerremote/example/main.go create mode 100644 samplers/jaegerremote/example/otel-collector.yaml create mode 100644 samplers/jaegerremote/example/strategies.json create mode 100644 samplers/jaegerremote/go.mod create mode 100644 samplers/jaegerremote/go.sum create mode 100644 samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go create mode 100644 samplers/jaegerremote/internal/testutils/mock_agent.go create mode 100644 samplers/jaegerremote/internal/testutils/mock_agent_test.go create mode 100644 samplers/jaegerremote/internal/testutils/sampling_manager.go create mode 100644 samplers/jaegerremote/internal/utils/http_json.go create mode 100644 samplers/jaegerremote/internal/utils/http_json_test.go create mode 100644 samplers/jaegerremote/internal/utils/rate_limiter.go create mode 100644 samplers/jaegerremote/internal/utils/rate_limiter_test.go create mode 100644 samplers/jaegerremote/jaeger-idl/proto/api_v2/sampling.proto create mode 100644 samplers/jaegerremote/sampler.go create mode 100644 samplers/jaegerremote/sampler_remote.go create mode 100644 samplers/jaegerremote/sampler_remote_options.go create mode 100644 samplers/jaegerremote/sampler_remote_test.go create mode 100644 samplers/jaegerremote/sampler_test.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 121cd2726ab..cac9eb7fa9e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -674,6 +674,24 @@ updates: schedule: interval: "weekly" day: "sunday" + - package-ecosystem: "gomod" + directory: "/samplers/jaegerremote" + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + interval: "weekly" + day: "sunday" + - package-ecosystem: "gomod" + directory: "/samplers/jaegerremote/example" + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + interval: "weekly" + day: "sunday" - package-ecosystem: "gomod" directory: "/tools" labels: diff --git a/samplers/jaegerremote/Makefile b/samplers/jaegerremote/Makefile new file mode 100644 index 00000000000..ee0231a2612 --- /dev/null +++ b/samplers/jaegerremote/Makefile @@ -0,0 +1,11 @@ +PROTOC=docker run --rm -v${PWD}:${PWD} -w${PWD} otel/build-protobuf:latest --proto_path=${PWD} + +PROTO_INCLUDES=-I/usr/include/github.com/gogo/protobuf + +.PHONY: proto-gen +proto-gen: + mkdir -p internal/proto-gen + + $(PROTOC) $(PROTO_INCLUDES) \ + --gogo_out=$(PWD)/internal/proto-gen \ + ${PWD}/jaeger-idl/proto/api_v2/sampling.proto diff --git a/samplers/jaegerremote/README.md b/samplers/jaegerremote/README.md new file mode 100644 index 00000000000..262b2c113f5 --- /dev/null +++ b/samplers/jaegerremote/README.md @@ -0,0 +1,38 @@ +# Jaeger Remote Sampler + +This package implements [Jaeger remote sampler](https://www.jaegertracing.io/docs/latest/sampling/#collector-sampling-configuration). + +## Example + +[example/](./example) shows how to host remote sampling strategies using the OpenTelemetry Collector. +The Collector uses the Jaeger receiver to host the strategy file. Note you do not need to run Jaeger to make use of the Jaeger remote sampling protocol. However, you do need Jaeger backend if you want to utilize its adaptive sampling engine that auto-calculates remote sampling strategies. + +Run the OpenTelemetry Collector using docker-compose: + +```shell +$ docker-compose up -d +``` + +You can fetch the strategy file using curl: + +```shell +$ curl 'localhost:5778/sampling?service=foo' +$ curl 'localhost:5778/sampling?service=myService' +``` + +Run the Go program. +This program will start with an initial sampling percentage of 50% and tries to fetch the sampling strategies from the OpenTelemetry Collector. +It will print the entire Jaeger remote sampler structure every 10 seconds, this allows you to observe the internal sampler. + +```shell +$ go run . +``` + +## Update generated Jaeger code + +Code is generated using the .proto files from [jaeger-idl](https://github.com/jaegertracing/jaeger-idl). +In case [sampling.proto](./jaeger-idl/proto/api_v2/sampling.proto) is modified these have to be regenerated. + +```shell +$ make proto-gen +``` diff --git a/samplers/jaegerremote/constants.go b/samplers/jaegerremote/constants.go new file mode 100644 index 00000000000..9b1bb9ea315 --- /dev/null +++ b/samplers/jaegerremote/constants.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "fmt" +) + +const ( + // defaultSamplingServerPort is the default port to fetch sampling config from, via http + defaultSamplingServerPort = 5778 +) + +var ( + // defaultSamplingServerURL is the default url to fetch sampling config from, via http + defaultSamplingServerURL = fmt.Sprintf("http://127.0.0.1:%d/sampling", defaultSamplingServerPort) +) diff --git a/samplers/jaegerremote/doc.go b/samplers/jaegerremote/doc.go new file mode 100644 index 00000000000..cd7232f16ea --- /dev/null +++ b/samplers/jaegerremote/doc.go @@ -0,0 +1,18 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote implements the Jaeger Remote protocol. +package jaegerremote // import "go.opentelemetry.io/contrib/samplers/jaegerremote" diff --git a/samplers/jaegerremote/example/docker-compose.yaml b/samplers/jaegerremote/example/docker-compose.yaml new file mode 100644 index 00000000000..7be0fa1b5b0 --- /dev/null +++ b/samplers/jaegerremote/example/docker-compose.yaml @@ -0,0 +1,11 @@ +version: "3" +services: + + otel-collector: + image: otel/opentelemetry-collector:latest + command: [ "--config=/etc/otel-collector.yaml" ] + volumes: + - ./otel-collector.yaml:/etc/otel-collector.yaml + - ./strategies.json:/etc/strategies.json + ports: + - "5778:5778" # default jaeger remote sampling port diff --git a/samplers/jaegerremote/example/go.mod b/samplers/jaegerremote/example/go.mod new file mode 100644 index 00000000000..3604e8dea72 --- /dev/null +++ b/samplers/jaegerremote/example/go.mod @@ -0,0 +1,13 @@ +module go.opentelemetry.io/contrib/samplers/jaegerremote/example + +go 1.16 + +require ( + github.com/davecgh/go-spew v1.1.1 + go.opentelemetry.io/contrib/samplers/jaegerremote v0.22.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 + go.opentelemetry.io/otel/sdk v1.4.1 +) + +replace go.opentelemetry.io/contrib/samplers/jaegerremote => ../ diff --git a/samplers/jaegerremote/example/go.sum b/samplers/jaegerremote/example/go.sum new file mode 100644 index 00000000000..1e00d51d80a --- /dev/null +++ b/samplers/jaegerremote/example/go.sum @@ -0,0 +1,168 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.1.0/go.mod h1:7cww0OW51jQ8IaZChIEdqLwgh+44+7uiTdWsAL0wQpA= +go.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g= +go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 h1:n9UCiD5XeG/a67Qvzsg9eRXB7DkysXtO7n8vSVnq2vI= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0/go.mod h1:lISWK4NRLxKH/IrroKBpMd7k/pBuUUaEU6bCykFb9hQ= +go.opentelemetry.io/otel/sdk v1.1.0/go.mod h1:3aQvM6uLm6C4wJpHtT8Od3vNzeZ34Pqc6bps8MywWzo= +go.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y= +go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= +go.opentelemetry.io/otel/trace v1.1.0/go.mod h1:i47XtdcBQiktu5IsrPqOHe8w+sBmnLwwHt8wiUsWGTI= +go.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ= +go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/samplers/jaegerremote/example/main.go b/samplers/jaegerremote/example/main.go new file mode 100644 index 00000000000..84075593b25 --- /dev/null +++ b/samplers/jaegerremote/example/main.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// +// 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 main + +import ( + "fmt" + "time" + + "github.com/davecgh/go-spew/spew" + + "go.opentelemetry.io/contrib/samplers/jaegerremote" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/sdk/trace" +) + +func main() { + jaegerRemoteSampler := jaegerremote.New( + "foo", + jaegerremote.WithSamplingServerURL("http://localhost:5778"), + jaegerremote.WithSamplingRefreshInterval(10*time.Second), // decrease polling interval to get quicker feedback + jaegerremote.WithInitialSampler(trace.TraceIDRatioBased(0.5)), + ) + + exporter, _ := stdouttrace.New() + + tp := trace.NewTracerProvider( + trace.WithSampler(jaegerRemoteSampler), + trace.WithSyncer(exporter), // for production usage, use trace.WithBatcher(exporter) + ) + otel.SetTracerProvider(tp) + + ticker := time.Tick(time.Second) + for { + <-ticker + fmt.Printf("\n* Jaeger Remote Sampler %v\n\n", time.Now()) + spewCfg := spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + } + spewCfg.Dump(jaegerRemoteSampler) + } +} diff --git a/samplers/jaegerremote/example/otel-collector.yaml b/samplers/jaegerremote/example/otel-collector.yaml new file mode 100644 index 00000000000..2a9407b424b --- /dev/null +++ b/samplers/jaegerremote/example/otel-collector.yaml @@ -0,0 +1,17 @@ +receivers: + jaeger: + protocols: + grpc: + remote_sampling: + host_endpoint: "0.0.0.0:5778" # default port + insecure: true + strategy_file: "/etc/strategies.json" + +exporters: + logging: + +service: + pipelines: + traces: + receivers: [ jaeger ] + exporters: [ logging ] diff --git a/samplers/jaegerremote/example/strategies.json b/samplers/jaegerremote/example/strategies.json new file mode 100644 index 00000000000..cbaf03f70d6 --- /dev/null +++ b/samplers/jaegerremote/example/strategies.json @@ -0,0 +1,30 @@ +{ + "service_strategies": [ + { + "service": "foo", + "type": "probabilistic", + "param": 0.8, + "operation_strategies": [ + { + "operation": "op1", + "type": "probabilistic", + "param": 0.2 + }, + { + "operation": "op2", + "type": "probabilistic", + "param": 0.4 + } + ] + }, + { + "service": "bar", + "type": "ratelimiting", + "param": 5 + } + ], + "default_strategy": { + "type": "probabilistic", + "param": 0.2 + } +} diff --git a/samplers/jaegerremote/go.mod b/samplers/jaegerremote/go.mod new file mode 100644 index 00000000000..43de2aad4b4 --- /dev/null +++ b/samplers/jaegerremote/go.mod @@ -0,0 +1,12 @@ +module go.opentelemetry.io/contrib/samplers/jaegerremote + +go 1.16 + +require ( + github.com/gogo/protobuf v1.3.2 + github.com/stretchr/testify v1.7.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa +) diff --git a/samplers/jaegerremote/go.sum b/samplers/jaegerremote/go.sum new file mode 100644 index 00000000000..617b39db733 --- /dev/null +++ b/samplers/jaegerremote/go.sum @@ -0,0 +1,162 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g= +go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= +go.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y= +go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= +go.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ= +go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go new file mode 100644 index 00000000000..5e6714ce30f --- /dev/null +++ b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go @@ -0,0 +1,1568 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: jaeger-idl/proto/api_v2/sampling.proto + +package api_v2 + +import ( + encoding_binary "encoding/binary" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type SamplingStrategyType int32 + +const ( + SamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0 + SamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1 +) + +var SamplingStrategyType_name = map[int32]string{ + 0: "PROBABILISTIC", + 1: "RATE_LIMITING", +} + +var SamplingStrategyType_value = map[string]int32{ + "PROBABILISTIC": 0, + "RATE_LIMITING": 1, +} + +func (x SamplingStrategyType) String() string { + return proto.EnumName(SamplingStrategyType_name, int32(x)) +} + +func (SamplingStrategyType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{0} +} + +type ProbabilisticSamplingStrategy struct { + SamplingRate float64 `protobuf:"fixed64,1,opt,name=samplingRate,proto3" json:"samplingRate,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ProbabilisticSamplingStrategy) Reset() { *m = ProbabilisticSamplingStrategy{} } +func (m *ProbabilisticSamplingStrategy) String() string { return proto.CompactTextString(m) } +func (*ProbabilisticSamplingStrategy) ProtoMessage() {} +func (*ProbabilisticSamplingStrategy) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{0} +} +func (m *ProbabilisticSamplingStrategy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProbabilisticSamplingStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProbabilisticSamplingStrategy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProbabilisticSamplingStrategy) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProbabilisticSamplingStrategy.Merge(m, src) +} +func (m *ProbabilisticSamplingStrategy) XXX_Size() int { + return m.Size() +} +func (m *ProbabilisticSamplingStrategy) XXX_DiscardUnknown() { + xxx_messageInfo_ProbabilisticSamplingStrategy.DiscardUnknown(m) +} + +var xxx_messageInfo_ProbabilisticSamplingStrategy proto.InternalMessageInfo + +func (m *ProbabilisticSamplingStrategy) GetSamplingRate() float64 { + if m != nil { + return m.SamplingRate + } + return 0 +} + +type RateLimitingSamplingStrategy struct { + MaxTracesPerSecond int32 `protobuf:"varint,1,opt,name=maxTracesPerSecond,proto3" json:"maxTracesPerSecond,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RateLimitingSamplingStrategy) Reset() { *m = RateLimitingSamplingStrategy{} } +func (m *RateLimitingSamplingStrategy) String() string { return proto.CompactTextString(m) } +func (*RateLimitingSamplingStrategy) ProtoMessage() {} +func (*RateLimitingSamplingStrategy) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{1} +} +func (m *RateLimitingSamplingStrategy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RateLimitingSamplingStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RateLimitingSamplingStrategy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RateLimitingSamplingStrategy) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitingSamplingStrategy.Merge(m, src) +} +func (m *RateLimitingSamplingStrategy) XXX_Size() int { + return m.Size() +} +func (m *RateLimitingSamplingStrategy) XXX_DiscardUnknown() { + xxx_messageInfo_RateLimitingSamplingStrategy.DiscardUnknown(m) +} + +var xxx_messageInfo_RateLimitingSamplingStrategy proto.InternalMessageInfo + +func (m *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int32 { + if m != nil { + return m.MaxTracesPerSecond + } + return 0 +} + +type OperationSamplingStrategy struct { + Operation string `protobuf:"bytes,1,opt,name=operation,proto3" json:"operation,omitempty"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `protobuf:"bytes,2,opt,name=probabilisticSampling,proto3" json:"probabilisticSampling,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OperationSamplingStrategy) Reset() { *m = OperationSamplingStrategy{} } +func (m *OperationSamplingStrategy) String() string { return proto.CompactTextString(m) } +func (*OperationSamplingStrategy) ProtoMessage() {} +func (*OperationSamplingStrategy) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{2} +} +func (m *OperationSamplingStrategy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *OperationSamplingStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_OperationSamplingStrategy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *OperationSamplingStrategy) XXX_Merge(src proto.Message) { + xxx_messageInfo_OperationSamplingStrategy.Merge(m, src) +} +func (m *OperationSamplingStrategy) XXX_Size() int { + return m.Size() +} +func (m *OperationSamplingStrategy) XXX_DiscardUnknown() { + xxx_messageInfo_OperationSamplingStrategy.DiscardUnknown(m) +} + +var xxx_messageInfo_OperationSamplingStrategy proto.InternalMessageInfo + +func (m *OperationSamplingStrategy) GetOperation() string { + if m != nil { + return m.Operation + } + return "" +} + +func (m *OperationSamplingStrategy) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if m != nil { + return m.ProbabilisticSampling + } + return nil +} + +type PerOperationSamplingStrategies struct { + DefaultSamplingProbability float64 `protobuf:"fixed64,1,opt,name=defaultSamplingProbability,proto3" json:"defaultSamplingProbability,omitempty"` + DefaultLowerBoundTracesPerSecond float64 `protobuf:"fixed64,2,opt,name=defaultLowerBoundTracesPerSecond,proto3" json:"defaultLowerBoundTracesPerSecond,omitempty"` + PerOperationStrategies []*OperationSamplingStrategy `protobuf:"bytes,3,rep,name=perOperationStrategies,proto3" json:"perOperationStrategies,omitempty"` + DefaultUpperBoundTracesPerSecond float64 `protobuf:"fixed64,4,opt,name=defaultUpperBoundTracesPerSecond,proto3" json:"defaultUpperBoundTracesPerSecond,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PerOperationSamplingStrategies) Reset() { *m = PerOperationSamplingStrategies{} } +func (m *PerOperationSamplingStrategies) String() string { return proto.CompactTextString(m) } +func (*PerOperationSamplingStrategies) ProtoMessage() {} +func (*PerOperationSamplingStrategies) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{3} +} +func (m *PerOperationSamplingStrategies) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PerOperationSamplingStrategies) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PerOperationSamplingStrategies.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PerOperationSamplingStrategies) XXX_Merge(src proto.Message) { + xxx_messageInfo_PerOperationSamplingStrategies.Merge(m, src) +} +func (m *PerOperationSamplingStrategies) XXX_Size() int { + return m.Size() +} +func (m *PerOperationSamplingStrategies) XXX_DiscardUnknown() { + xxx_messageInfo_PerOperationSamplingStrategies.DiscardUnknown(m) +} + +var xxx_messageInfo_PerOperationSamplingStrategies proto.InternalMessageInfo + +func (m *PerOperationSamplingStrategies) GetDefaultSamplingProbability() float64 { + if m != nil { + return m.DefaultSamplingProbability + } + return 0 +} + +func (m *PerOperationSamplingStrategies) GetDefaultLowerBoundTracesPerSecond() float64 { + if m != nil { + return m.DefaultLowerBoundTracesPerSecond + } + return 0 +} + +func (m *PerOperationSamplingStrategies) GetPerOperationStrategies() []*OperationSamplingStrategy { + if m != nil { + return m.PerOperationStrategies + } + return nil +} + +func (m *PerOperationSamplingStrategies) GetDefaultUpperBoundTracesPerSecond() float64 { + if m != nil { + return m.DefaultUpperBoundTracesPerSecond + } + return 0 +} + +type SamplingStrategyResponse struct { + StrategyType SamplingStrategyType `protobuf:"varint,1,opt,name=strategyType,proto3,enum=jaeger.api_v2.SamplingStrategyType" json:"strategyType,omitempty"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `protobuf:"bytes,2,opt,name=probabilisticSampling,proto3" json:"probabilisticSampling,omitempty"` + RateLimitingSampling *RateLimitingSamplingStrategy `protobuf:"bytes,3,opt,name=rateLimitingSampling,proto3" json:"rateLimitingSampling,omitempty"` + OperationSampling *PerOperationSamplingStrategies `protobuf:"bytes,4,opt,name=operationSampling,proto3" json:"operationSampling,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SamplingStrategyResponse) Reset() { *m = SamplingStrategyResponse{} } +func (m *SamplingStrategyResponse) String() string { return proto.CompactTextString(m) } +func (*SamplingStrategyResponse) ProtoMessage() {} +func (*SamplingStrategyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{4} +} +func (m *SamplingStrategyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SamplingStrategyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SamplingStrategyResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SamplingStrategyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SamplingStrategyResponse.Merge(m, src) +} +func (m *SamplingStrategyResponse) XXX_Size() int { + return m.Size() +} +func (m *SamplingStrategyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SamplingStrategyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SamplingStrategyResponse proto.InternalMessageInfo + +func (m *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType { + if m != nil { + return m.StrategyType + } + return SamplingStrategyType_PROBABILISTIC +} + +func (m *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if m != nil { + return m.ProbabilisticSampling + } + return nil +} + +func (m *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy { + if m != nil { + return m.RateLimitingSampling + } + return nil +} + +func (m *SamplingStrategyResponse) GetOperationSampling() *PerOperationSamplingStrategies { + if m != nil { + return m.OperationSampling + } + return nil +} + +type SamplingStrategyParameters struct { + ServiceName string `protobuf:"bytes,1,opt,name=serviceName,proto3" json:"serviceName,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SamplingStrategyParameters) Reset() { *m = SamplingStrategyParameters{} } +func (m *SamplingStrategyParameters) String() string { return proto.CompactTextString(m) } +func (*SamplingStrategyParameters) ProtoMessage() {} +func (*SamplingStrategyParameters) Descriptor() ([]byte, []int) { + return fileDescriptor_ae32d90db01957f7, []int{5} +} +func (m *SamplingStrategyParameters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SamplingStrategyParameters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SamplingStrategyParameters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SamplingStrategyParameters) XXX_Merge(src proto.Message) { + xxx_messageInfo_SamplingStrategyParameters.Merge(m, src) +} +func (m *SamplingStrategyParameters) XXX_Size() int { + return m.Size() +} +func (m *SamplingStrategyParameters) XXX_DiscardUnknown() { + xxx_messageInfo_SamplingStrategyParameters.DiscardUnknown(m) +} + +var xxx_messageInfo_SamplingStrategyParameters proto.InternalMessageInfo + +func (m *SamplingStrategyParameters) GetServiceName() string { + if m != nil { + return m.ServiceName + } + return "" +} + +func init() { + proto.RegisterEnum("jaeger.api_v2.SamplingStrategyType", SamplingStrategyType_name, SamplingStrategyType_value) + proto.RegisterType((*ProbabilisticSamplingStrategy)(nil), "jaeger.api_v2.ProbabilisticSamplingStrategy") + proto.RegisterType((*RateLimitingSamplingStrategy)(nil), "jaeger.api_v2.RateLimitingSamplingStrategy") + proto.RegisterType((*OperationSamplingStrategy)(nil), "jaeger.api_v2.OperationSamplingStrategy") + proto.RegisterType((*PerOperationSamplingStrategies)(nil), "jaeger.api_v2.PerOperationSamplingStrategies") + proto.RegisterType((*SamplingStrategyResponse)(nil), "jaeger.api_v2.SamplingStrategyResponse") + proto.RegisterType((*SamplingStrategyParameters)(nil), "jaeger.api_v2.SamplingStrategyParameters") +} + +func init() { + proto.RegisterFile("jaeger-idl/proto/api_v2/sampling.proto", fileDescriptor_ae32d90db01957f7) +} + +var fileDescriptor_ae32d90db01957f7 = []byte{ + // 577 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xcf, 0x6f, 0x12, 0x41, + 0x14, 0x76, 0x40, 0x9b, 0xf4, 0xd1, 0x6a, 0x3b, 0x56, 0x5d, 0x09, 0x25, 0x64, 0x9b, 0x28, 0x56, + 0x81, 0x64, 0xbd, 0x19, 0xd3, 0xa4, 0x34, 0x86, 0xac, 0xa1, 0x94, 0x2c, 0x78, 0xd1, 0x03, 0x0e, + 0xf0, 0xdc, 0x8c, 0x81, 0x9d, 0xcd, 0xec, 0xb4, 0xca, 0xd5, 0xc4, 0xab, 0x17, 0xcf, 0x5e, 0xfc, + 0x6b, 0x3c, 0x9a, 0x78, 0xf3, 0x64, 0x88, 0x7f, 0x88, 0xd9, 0x5f, 0x2d, 0x2c, 0x0b, 0xdc, 0x3c, + 0xed, 0xe6, 0xbd, 0x6f, 0xbe, 0xef, 0x7b, 0x6f, 0xde, 0x3c, 0x78, 0xf0, 0x9e, 0xa1, 0x8d, 0xb2, + 0xc2, 0x87, 0xa3, 0x9a, 0x2b, 0x85, 0x12, 0x35, 0xe6, 0xf2, 0xde, 0x85, 0x51, 0xf3, 0xd8, 0xd8, + 0x1d, 0x71, 0xc7, 0xae, 0x06, 0x51, 0xba, 0x1d, 0xe2, 0xaa, 0x61, 0x36, 0xbf, 0x67, 0x0b, 0x5b, + 0x84, 0x78, 0xff, 0x2f, 0x04, 0xe5, 0x0b, 0xb6, 0x10, 0xf6, 0x08, 0x7d, 0x8a, 0x1a, 0x73, 0x1c, + 0xa1, 0x98, 0xe2, 0xc2, 0xf1, 0xc2, 0xac, 0x7e, 0x02, 0xfb, 0x6d, 0x29, 0xfa, 0xac, 0xcf, 0x47, + 0xdc, 0x53, 0x7c, 0xd0, 0x89, 0x14, 0x3a, 0x4a, 0x32, 0x85, 0xf6, 0x84, 0xea, 0xb0, 0x15, 0xab, + 0x5a, 0x4c, 0xa1, 0x46, 0x4a, 0xa4, 0x4c, 0xac, 0xb9, 0x98, 0xde, 0x82, 0x82, 0xff, 0x6d, 0xf2, + 0x31, 0x57, 0xfe, 0xd9, 0x24, 0x47, 0x15, 0xe8, 0x98, 0x7d, 0xec, 0x4a, 0x36, 0x40, 0xaf, 0x8d, + 0xb2, 0x83, 0x03, 0xe1, 0x0c, 0x03, 0xa6, 0x1b, 0x56, 0x4a, 0x46, 0xff, 0x46, 0xe0, 0xfe, 0x99, + 0x8b, 0x32, 0x70, 0xba, 0xc0, 0x56, 0x80, 0x4d, 0x11, 0x27, 0x03, 0x92, 0x4d, 0xeb, 0x2a, 0x40, + 0xfb, 0x70, 0xc7, 0x4d, 0x2b, 0x48, 0xcb, 0x94, 0x48, 0x39, 0x67, 0x3c, 0xa9, 0xce, 0xf5, 0xac, + 0xba, 0xb2, 0x78, 0x2b, 0x9d, 0x4a, 0xff, 0x9d, 0x81, 0x62, 0x1b, 0xe5, 0x32, 0x8b, 0x1c, 0x3d, + 0x7a, 0x04, 0xf9, 0x21, 0xbe, 0x63, 0xe7, 0x23, 0x15, 0x27, 0x2f, 0x95, 0xd4, 0x24, 0x6a, 0xe2, + 0x0a, 0x04, 0x7d, 0x09, 0xa5, 0x28, 0xdb, 0x14, 0x1f, 0x50, 0xd6, 0xc5, 0xb9, 0x33, 0x4c, 0x36, + 0x30, 0x13, 0xb0, 0xac, 0xc5, 0xd1, 0xb7, 0x70, 0xd7, 0x9d, 0x75, 0x7b, 0xe9, 0x52, 0xcb, 0x96, + 0xb2, 0xe5, 0x9c, 0x51, 0x4e, 0xf4, 0x64, 0x69, 0xeb, 0xad, 0x25, 0x3c, 0x33, 0x6e, 0x5f, 0xb9, + 0xee, 0x12, 0xb7, 0xd7, 0xe7, 0xdc, 0x2e, 0xc5, 0xe9, 0x9f, 0xb3, 0xa0, 0x2d, 0x08, 0xa3, 0xe7, + 0x0a, 0xc7, 0x43, 0xda, 0x80, 0x2d, 0x2f, 0x8a, 0x75, 0x27, 0x6e, 0x38, 0x8d, 0x37, 0x8d, 0x83, + 0x44, 0x01, 0xc9, 0xe3, 0x3e, 0xd4, 0x9a, 0x3b, 0xf8, 0x3f, 0xc6, 0x84, 0xf6, 0x60, 0x4f, 0xa6, + 0x3c, 0x0b, 0x2d, 0x1b, 0x48, 0x3c, 0x4e, 0x48, 0xac, 0x7a, 0x41, 0x56, 0x2a, 0x11, 0x7d, 0x03, + 0xbb, 0x22, 0x79, 0x57, 0x41, 0x9f, 0x73, 0x46, 0x25, 0x59, 0xc0, 0xca, 0x71, 0xb5, 0x16, 0x79, + 0xf4, 0x23, 0xc8, 0x27, 0x6d, 0xb4, 0x99, 0x64, 0x63, 0x54, 0x28, 0x3d, 0x5a, 0x82, 0x9c, 0x87, + 0xf2, 0x82, 0x0f, 0xb0, 0xc5, 0xc6, 0x18, 0x3d, 0xc3, 0xd9, 0xd0, 0xe1, 0x73, 0xd8, 0x4b, 0xbb, + 0x07, 0xba, 0x0b, 0xdb, 0x6d, 0xeb, 0xac, 0x7e, 0x5c, 0x37, 0x9b, 0x66, 0xa7, 0x6b, 0x9e, 0xec, + 0x5c, 0xf3, 0x43, 0xd6, 0x71, 0xf7, 0x45, 0xaf, 0x69, 0x9e, 0x9a, 0x5d, 0xb3, 0xd5, 0xd8, 0x21, + 0xc6, 0x77, 0x02, 0xb7, 0xe2, 0xe3, 0xa7, 0xcc, 0x61, 0x36, 0x4a, 0xfa, 0x85, 0xc0, 0xed, 0x06, + 0xaa, 0x85, 0x85, 0xf0, 0x68, 0xcd, 0xf5, 0x5f, 0xd9, 0xce, 0x3f, 0x5c, 0x03, 0x8d, 0x07, 0x4d, + 0x3f, 0xf8, 0xf4, 0xeb, 0xef, 0xd7, 0xcc, 0xbe, 0xae, 0x05, 0x7b, 0x73, 0x66, 0xf5, 0xc6, 0xc8, + 0x67, 0xe4, 0xb0, 0x5e, 0xf9, 0x31, 0x2d, 0x92, 0x9f, 0xd3, 0x22, 0xf9, 0x33, 0x2d, 0x12, 0xb8, + 0xc7, 0x45, 0xc4, 0xae, 0x24, 0x1b, 0xf8, 0x5b, 0x3a, 0x14, 0x79, 0xbd, 0x11, 0x7e, 0xfb, 0x1b, + 0xc1, 0xca, 0x7d, 0xfa, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x60, 0xd2, 0xcd, 0xa7, 0xdf, 0x05, 0x00, + 0x00, +} + +func (m *ProbabilisticSamplingStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProbabilisticSamplingStrategy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProbabilisticSamplingStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.SamplingRate != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.SamplingRate)))) + i-- + dAtA[i] = 0x9 + } + return len(dAtA) - i, nil +} + +func (m *RateLimitingSamplingStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RateLimitingSamplingStrategy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RateLimitingSamplingStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MaxTracesPerSecond != 0 { + i = encodeVarintSampling(dAtA, i, uint64(m.MaxTracesPerSecond)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *OperationSamplingStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OperationSamplingStrategy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OperationSamplingStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.ProbabilisticSampling != nil { + { + size, err := m.ProbabilisticSampling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSampling(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Operation) > 0 { + i -= len(m.Operation) + copy(dAtA[i:], m.Operation) + i = encodeVarintSampling(dAtA, i, uint64(len(m.Operation))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PerOperationSamplingStrategies) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PerOperationSamplingStrategies) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PerOperationSamplingStrategies) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.DefaultUpperBoundTracesPerSecond != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.DefaultUpperBoundTracesPerSecond)))) + i-- + dAtA[i] = 0x21 + } + if len(m.PerOperationStrategies) > 0 { + for iNdEx := len(m.PerOperationStrategies) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PerOperationStrategies[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSampling(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.DefaultLowerBoundTracesPerSecond != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.DefaultLowerBoundTracesPerSecond)))) + i-- + dAtA[i] = 0x11 + } + if m.DefaultSamplingProbability != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.DefaultSamplingProbability)))) + i-- + dAtA[i] = 0x9 + } + return len(dAtA) - i, nil +} + +func (m *SamplingStrategyResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SamplingStrategyResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SamplingStrategyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.OperationSampling != nil { + { + size, err := m.OperationSampling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSampling(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.RateLimitingSampling != nil { + { + size, err := m.RateLimitingSampling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSampling(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.ProbabilisticSampling != nil { + { + size, err := m.ProbabilisticSampling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSampling(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.StrategyType != 0 { + i = encodeVarintSampling(dAtA, i, uint64(m.StrategyType)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SamplingStrategyParameters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SamplingStrategyParameters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SamplingStrategyParameters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.ServiceName) > 0 { + i -= len(m.ServiceName) + copy(dAtA[i:], m.ServiceName) + i = encodeVarintSampling(dAtA, i, uint64(len(m.ServiceName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSampling(dAtA []byte, offset int, v uint64) int { + offset -= sovSampling(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ProbabilisticSamplingStrategy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SamplingRate != 0 { + n += 9 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RateLimitingSamplingStrategy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MaxTracesPerSecond != 0 { + n += 1 + sovSampling(uint64(m.MaxTracesPerSecond)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *OperationSamplingStrategy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Operation) + if l > 0 { + n += 1 + l + sovSampling(uint64(l)) + } + if m.ProbabilisticSampling != nil { + l = m.ProbabilisticSampling.Size() + n += 1 + l + sovSampling(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PerOperationSamplingStrategies) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DefaultSamplingProbability != 0 { + n += 9 + } + if m.DefaultLowerBoundTracesPerSecond != 0 { + n += 9 + } + if len(m.PerOperationStrategies) > 0 { + for _, e := range m.PerOperationStrategies { + l = e.Size() + n += 1 + l + sovSampling(uint64(l)) + } + } + if m.DefaultUpperBoundTracesPerSecond != 0 { + n += 9 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *SamplingStrategyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StrategyType != 0 { + n += 1 + sovSampling(uint64(m.StrategyType)) + } + if m.ProbabilisticSampling != nil { + l = m.ProbabilisticSampling.Size() + n += 1 + l + sovSampling(uint64(l)) + } + if m.RateLimitingSampling != nil { + l = m.RateLimitingSampling.Size() + n += 1 + l + sovSampling(uint64(l)) + } + if m.OperationSampling != nil { + l = m.OperationSampling.Size() + n += 1 + l + sovSampling(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *SamplingStrategyParameters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceName) + if l > 0 { + n += 1 + l + sovSampling(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovSampling(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSampling(x uint64) (n int) { + return sovSampling(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ProbabilisticSamplingStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProbabilisticSamplingStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProbabilisticSamplingStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field SamplingRate", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.SamplingRate = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RateLimitingSamplingStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RateLimitingSamplingStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RateLimitingSamplingStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTracesPerSecond", wireType) + } + m.MaxTracesPerSecond = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTracesPerSecond |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OperationSamplingStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OperationSamplingStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OperationSamplingStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Operation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Operation = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProbabilisticSampling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProbabilisticSampling == nil { + m.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + } + if err := m.ProbabilisticSampling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PerOperationSamplingStrategies) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PerOperationSamplingStrategies: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PerOperationSamplingStrategies: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSamplingProbability", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.DefaultSamplingProbability = float64(math.Float64frombits(v)) + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultLowerBoundTracesPerSecond", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.DefaultLowerBoundTracesPerSecond = float64(math.Float64frombits(v)) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PerOperationStrategies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PerOperationStrategies = append(m.PerOperationStrategies, &OperationSamplingStrategy{}) + if err := m.PerOperationStrategies[len(m.PerOperationStrategies)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultUpperBoundTracesPerSecond", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.DefaultUpperBoundTracesPerSecond = float64(math.Float64frombits(v)) + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SamplingStrategyResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SamplingStrategyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SamplingStrategyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StrategyType", wireType) + } + m.StrategyType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StrategyType |= SamplingStrategyType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProbabilisticSampling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProbabilisticSampling == nil { + m.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + } + if err := m.ProbabilisticSampling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RateLimitingSampling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RateLimitingSampling == nil { + m.RateLimitingSampling = &RateLimitingSamplingStrategy{} + } + if err := m.RateLimitingSampling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OperationSampling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OperationSampling == nil { + m.OperationSampling = &PerOperationSamplingStrategies{} + } + if err := m.OperationSampling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SamplingStrategyParameters) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SamplingStrategyParameters: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SamplingStrategyParameters: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSampling + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSampling + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSampling + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServiceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSampling(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSampling + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSampling(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSampling + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSampling + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSampling + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSampling + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSampling + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSampling + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSampling = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSampling = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSampling = fmt.Errorf("proto: unexpected end of group") +) diff --git a/samplers/jaegerremote/internal/testutils/mock_agent.go b/samplers/jaegerremote/internal/testutils/mock_agent.go new file mode 100644 index 00000000000..0e5c6690821 --- /dev/null +++ b/samplers/jaegerremote/internal/testutils/mock_agent.go @@ -0,0 +1,91 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 testutils + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" +) + +// StartMockAgent runs a mock representation of jaeger-agent. +// This function returns a started server. +func StartMockAgent() (*MockAgent, error) { + samplingManager := newSamplingManager() + samplingHandler := &samplingHandler{manager: samplingManager} + samplingServer := httptest.NewServer(samplingHandler) + + agent := &MockAgent{ + samplingMgr: samplingManager, + samplingSrv: samplingServer, + } + + return agent, nil +} + +// Close stops the serving of traffic +func (s *MockAgent) Close() { + s.samplingSrv.Close() +} + +// MockAgent is a mock representation of Jaeger Agent. +// It has an HTTP endpoint for sampling strategies. +type MockAgent struct { + samplingMgr *samplingManager + samplingSrv *httptest.Server +} + +// SamplingServerAddr returns the host:port of HTTP server exposing sampling strategy endpoint +func (s *MockAgent) SamplingServerAddr() string { + return s.samplingSrv.Listener.Addr().String() +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *MockAgent) AddSamplingStrategy(service string, strategy interface{}) { + s.samplingMgr.AddSamplingStrategy(service, strategy) +} + +type samplingHandler struct { + manager *samplingManager +} + +func (h *samplingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + services := r.URL.Query()["service"] + if len(services) == 0 { + http.Error(w, "'service' parameter is empty", http.StatusBadRequest) + return + } + if len(services) > 1 { + http.Error(w, "'service' parameter must occur only once", http.StatusBadRequest) + return + } + resp, err := h.manager.GetSamplingStrategy(services[0]) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving strategy: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, "Cannot marshall Thrift to JSON", http.StatusInternalServerError) + return + } + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/samplers/jaegerremote/internal/testutils/mock_agent_test.go b/samplers/jaegerremote/internal/testutils/mock_agent_test.go new file mode 100644 index 00000000000..f6e54b2fd97 --- /dev/null +++ b/samplers/jaegerremote/internal/testutils/mock_agent_test.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" + "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/utils" +) + +func TestMockAgentSamplingManager(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/", nil) + require.Error(t, err, "no 'service' parameter") + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=a&service=b", nil) + require.Error(t, err, "Too many 'service' parameters") + + var resp jaeger_api_v2.SamplingStrategyResponse + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=something", &resp) + require.NoError(t, err) + assert.Equal(t, jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, resp.StrategyType) + + mockAgent.AddSamplingStrategy("service123", &jaeger_api_v2.SamplingStrategyResponse{ + StrategyType: jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &jaeger_api_v2.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: 123, + }, + }) + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=service123", &resp) + require.NoError(t, err) + assert.Equal(t, jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, resp.StrategyType) + require.NotNil(t, resp.RateLimitingSampling) + assert.EqualValues(t, 123, resp.RateLimitingSampling.MaxTracesPerSecond) +} diff --git a/samplers/jaegerremote/internal/testutils/sampling_manager.go b/samplers/jaegerremote/internal/testutils/sampling_manager.go new file mode 100644 index 00000000000..adcfea1fff6 --- /dev/null +++ b/samplers/jaegerremote/internal/testutils/sampling_manager.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 testutils + +import ( + "sync" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" +) + +func newSamplingManager() *samplingManager { + return &samplingManager{ + sampling: make(map[string]interface{}), + } +} + +type samplingManager struct { + sampling map[string]interface{} + mutex sync.Mutex +} + +// GetSamplingStrategy implements handler method of sampling.SamplingManager +func (s *samplingManager) GetSamplingStrategy(serviceName string) (interface{}, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + if strategy, ok := s.sampling[serviceName]; ok { + return strategy, nil + } + return &jaeger_api_v2.SamplingStrategyResponse{ + StrategyType: jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 0.01, + }}, nil +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *samplingManager) AddSamplingStrategy(service string, strategy interface{}) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.sampling[service] = strategy +} diff --git a/samplers/jaegerremote/internal/utils/http_json.go b/samplers/jaegerremote/internal/utils/http_json.go new file mode 100644 index 00000000000..fe5f7d76dc0 --- /dev/null +++ b/samplers/jaegerremote/internal/utils/http_json.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// GetJSON makes an HTTP call to the specified URL and parses the returned JSON into `out`. +func GetJSON(url string, out interface{}) error { + resp, err := http.Get(url) + if err != nil { + return err + } + return ReadJSON(resp, out) +} + +// ReadJSON reads JSON from http.Response and parses it into `out` +func ReadJSON(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body) + } + + if out == nil { + _, err := io.Copy(ioutil.Discard, resp.Body) + if err != nil { + return err + } + return nil + } + + decoder := json.NewDecoder(resp.Body) + return decoder.Decode(out) +} diff --git a/samplers/jaegerremote/internal/utils/http_json_test.go b/samplers/jaegerremote/internal/utils/http_json_test.go new file mode 100644 index 00000000000..12c00f81fb1 --- /dev/null +++ b/samplers/jaegerremote/internal/utils/http_json_test.go @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testJSONStruct struct { + Name string + Age int +} + +func TestGetJSON(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + _, err := w.Write([]byte("{\"name\": \"Bender\", \"age\": 3}")) + assert.NoError(t, err) + })) + defer server.Close() + + var s testJSONStruct + err := GetJSON(server.URL, &s) + require.NoError(t, err) + + assert.Equal(t, "Bender", s.Name) + assert.Equal(t, 3, s.Age) +} + +func TestGetJSONErrors(t *testing.T) { + var s testJSONStruct + err := GetJSON("localhost:0", &s) + assert.Error(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "some error", http.StatusInternalServerError) + })) + defer server.Close() + + err = GetJSON(server.URL, &s) + assert.Error(t, err) +} diff --git a/samplers/jaegerremote/internal/utils/rate_limiter.go b/samplers/jaegerremote/internal/utils/rate_limiter.go new file mode 100644 index 00000000000..56bd5e6f8b6 --- /dev/null +++ b/samplers/jaegerremote/internal/utils/rate_limiter.go @@ -0,0 +1,107 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 ( + "sync" + "time" +) + +// RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. +// + +// RateLimiter is a rate limiter based on leaky bucket algorithm, formulated in terms of a +// credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional +// to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost +// of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" +// and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. +// +// This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the +// max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message +// to determine if the message is within the rate limit. +// +// It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput +// as bytes/second, and calling CheckCredit() with the actual message size. +// +type RateLimiter struct { + lock sync.Mutex + + creditsPerSecond float64 + balance float64 + maxBalance float64 + lastTick time.Time + + timeNow func() time.Time +} + +// NewRateLimiter creates a new RateLimiter. +func NewRateLimiter(creditsPerSecond, maxBalance float64) *RateLimiter { + return &RateLimiter{ + creditsPerSecond: creditsPerSecond, + balance: maxBalance, + maxBalance: maxBalance, + lastTick: time.Now(), + timeNow: time.Now, + } +} + +// CheckCredit tries to reduce the current balance by itemCost provided that the current balance +// is not lest than itemCost. +func (rl *RateLimiter) CheckCredit(itemCost float64) bool { + rl.lock.Lock() + defer rl.lock.Unlock() + + // if we have enough credits to pay for current item, then reduce balance and allow + if rl.balance >= itemCost { + rl.balance -= itemCost + return true + } + // otherwise check if balance can be increased due to time elapsed, and try again + rl.updateBalance() + if rl.balance >= itemCost { + rl.balance -= itemCost + return true + } + return false +} + +// updateBalance recalculates current balance based on time elapsed. Must be called while holding a lock. +func (rl *RateLimiter) updateBalance() { + // calculate how much time passed since the last tick, and update current tick + currentTime := rl.timeNow() + elapsedTime := currentTime.Sub(rl.lastTick) + rl.lastTick = currentTime + // calculate how much credit have we accumulated since the last tick + rl.balance += elapsedTime.Seconds() * rl.creditsPerSecond + if rl.balance > rl.maxBalance { + rl.balance = rl.maxBalance + } +} + +// Update changes the main parameters of the rate limiter in-place, while retaining +// the current accumulated balance (pro-rated to the new maxBalance value). Using this method +// instead of creating a new rate limiter helps to avoid thundering herd when sampling +// strategies are updated. +func (rl *RateLimiter) Update(creditsPerSecond, maxBalance float64) { + rl.lock.Lock() + defer rl.lock.Unlock() + + rl.updateBalance() // get up to date balance + rl.balance = rl.balance * maxBalance / rl.maxBalance + rl.creditsPerSecond = creditsPerSecond + rl.maxBalance = maxBalance +} diff --git a/samplers/jaegerremote/internal/utils/rate_limiter_test.go b/samplers/jaegerremote/internal/utils/rate_limiter_test.go new file mode 100644 index 00000000000..fb815f4d0e1 --- /dev/null +++ b/samplers/jaegerremote/internal/utils/rate_limiter_test.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter(t *testing.T) { + rl := NewRateLimiter(2.0, 2.0) + // stop time + ts := time.Now() + rl.lastTick = ts + rl.timeNow = func() time.Time { + return ts + } + assert.True(t, rl.CheckCredit(1.0)) + assert.True(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) + // move time 250ms forward, not enough credits to pay for 1.0 item + rl.timeNow = func() time.Time { + return ts.Add(time.Second / 4) + } + assert.False(t, rl.CheckCredit(1.0)) + // move time 500ms forward, now enough credits to pay for 1.0 item + rl.timeNow = func() time.Time { + return ts.Add(time.Second/4 + time.Second/2) + } + assert.True(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) + // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 + rl.lastTick = ts + rl.timeNow = func() time.Time { + return ts.Add(5 * time.Second) + } + assert.True(t, rl.CheckCredit(1.0)) + assert.True(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) +} + +func TestRateLimiterMaxBalance(t *testing.T) { + rl := NewRateLimiter(0.1, 1.0) + // stop time + ts := time.Now() + rl.lastTick = ts + rl.timeNow = func() time.Time { + return ts + } + assert.True(t, rl.CheckCredit(1.0), "on initialization, should have enough credits for 1 message") + + // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 + rl.timeNow = func() time.Time { + return ts.Add(time.Second * 20) + } + assert.True(t, rl.CheckCredit(1.0)) + assert.False(t, rl.CheckCredit(1.0)) +} + +func TestRateLimiterReconfigure(t *testing.T) { + rl := NewRateLimiter(1, 1.0) + assertBalance := func(expected float64) { + const delta = 0.0000001 // just some precision for comparing floats + assert.InDelta(t, expected, rl.balance, delta) + } + // stop time + ts := time.Now() + rl.lastTick = ts + rl.timeNow = func() time.Time { + return ts + } + assert.True(t, rl.CheckCredit(1.0), "first message must succeed") + assert.False(t, rl.CheckCredit(1.0), "second message must be rejected") + assertBalance(0.0) + + // move half-second forward + rl.timeNow = func() time.Time { + return ts.Add(time.Second / 2) + } + rl.updateBalance() + assertBalance(0.5) // 50% of max + + rl.Update(2, 4) + assertBalance(2) // 50% of max + assert.EqualValues(t, 2, rl.creditsPerSecond) + assert.EqualValues(t, 4, rl.maxBalance) +} diff --git a/samplers/jaegerremote/jaeger-idl/proto/api_v2/sampling.proto b/samplers/jaegerremote/jaeger-idl/proto/api_v2/sampling.proto new file mode 100644 index 00000000000..c97f541175e --- /dev/null +++ b/samplers/jaegerremote/jaeger-idl/proto/api_v2/sampling.proto @@ -0,0 +1,79 @@ +// Copyright (c) 2019 The Jaeger Authors. +// Copyright (c) 2018 Uber Technologies, Inc. +// +// 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. + +// This package implements the Jaeger jaeger-idl specification as defined +// at https://github.com/jaegertracing/jaeger-idl/blob/main/proto/api_v2/sampling.proto +syntax="proto3"; + +package jaeger.api_v2; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; + +option go_package = "api_v2"; +option java_package = "io.jaegertracing.api_v2"; + +// Enable gogoprotobuf extensions (https://github.com/gogo/protobuf/blob/master/extensions.md). +// Enable custom Marshal method. +option (gogoproto.marshaler_all) = true; +// Enable custom Unmarshal method. +option (gogoproto.unmarshaler_all) = true; +// Enable custom Size method (Required by Marshal and Unmarshal). +option (gogoproto.sizer_all) = true; + +enum SamplingStrategyType { + PROBABILISTIC = 0; + RATE_LIMITING = 1; +}; + +message ProbabilisticSamplingStrategy { + double samplingRate = 1; +} + +message RateLimitingSamplingStrategy { + int32 maxTracesPerSecond = 1; +} + +message OperationSamplingStrategy { + string operation = 1; + ProbabilisticSamplingStrategy probabilisticSampling = 2; +} + +message PerOperationSamplingStrategies { + double defaultSamplingProbability = 1; + double defaultLowerBoundTracesPerSecond = 2; + repeated OperationSamplingStrategy perOperationStrategies = 3; + double defaultUpperBoundTracesPerSecond = 4; +} + +message SamplingStrategyResponse { + SamplingStrategyType strategyType = 1; + ProbabilisticSamplingStrategy probabilisticSampling = 2; + RateLimitingSamplingStrategy rateLimitingSampling = 3; + PerOperationSamplingStrategies operationSampling =4; +} + +message SamplingStrategyParameters { + string serviceName = 1; +} + +service SamplingManager { + rpc GetSamplingStrategy(SamplingStrategyParameters) returns (SamplingStrategyResponse) { + option (google.api.http) = { + post: "/api/v2/samplingStrategy" + body: "*" + }; + } +} diff --git a/samplers/jaegerremote/sampler.go b/samplers/jaegerremote/sampler.go new file mode 100644 index 00000000000..1decbb6e41d --- /dev/null +++ b/samplers/jaegerremote/sampler.go @@ -0,0 +1,334 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "encoding/binary" + "fmt" + "math" + "sync" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" + "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/utils" + "go.opentelemetry.io/otel/sdk/trace" + oteltrace "go.opentelemetry.io/otel/trace" +) + +const ( + defaultMaxOperations = 2000 +) + +// ----------------------- + +// probabilisticSampler is a sampler that randomly samples a certain percentage +// of traces. +type probabilisticSampler struct { + samplingRate float64 + samplingBoundary uint64 +} + +const maxRandomNumber = ^(uint64(1) << 63) // i.e. 0x7fffffffffffffff + +// newProbabilisticSampler creates a sampler that randomly samples a certain percentage of traces specified by the +// samplingRate, in the range between 0.0 and 1.0. +// +// It relies on the fact that new trace IDs are 63bit random numbers themselves, thus making the sampling decision +// without generating a new random number, but simply calculating if traceID < (samplingRate * 2^63). +func newProbabilisticSampler(samplingRate float64) *probabilisticSampler { + s := new(probabilisticSampler) + return s.init(samplingRate) +} + +func (s *probabilisticSampler) init(samplingRate float64) *probabilisticSampler { + s.samplingRate = math.Max(0.0, math.Min(samplingRate, 1.0)) + s.samplingBoundary = uint64(float64(maxRandomNumber) * s.samplingRate) + return s +} + +// SamplingRate returns the sampling probability this sampled was constructed with. +func (s *probabilisticSampler) SamplingRate() float64 { + return s.samplingRate +} + +func (s *probabilisticSampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult { + psc := oteltrace.SpanContextFromContext(p.ParentContext) + traceID := binary.BigEndian.Uint64(p.TraceID[0:8]) + if s.samplingBoundary >= traceID&maxRandomNumber { + return trace.SamplingResult{ + Decision: trace.RecordAndSample, + Tracestate: psc.TraceState(), + } + } + return trace.SamplingResult{ + Decision: trace.Drop, + Tracestate: psc.TraceState(), + } +} + +// Equal compares with another sampler. +func (s *probabilisticSampler) Equal(other trace.Sampler) bool { + if o, ok := other.(*probabilisticSampler); ok { + return s.samplingBoundary == o.samplingBoundary + } + return false +} + +// Update modifies in-place the sampling rate. Locking must be done externally. +func (s *probabilisticSampler) Update(samplingRate float64) error { + if samplingRate < 0.0 || samplingRate > 1.0 { + return fmt.Errorf("Sampling Rate must be between 0.0 and 1.0, received %f", samplingRate) + } + s.init(samplingRate) + return nil +} + +func (s *probabilisticSampler) Description() string { + return "probabilisticSampler{}" +} + +// ----------------------- + +// rateLimitingSampler samples at most maxTracesPerSecond. The distribution of sampled traces follows +// burstiness of the service, i.e. a service with uniformly distributed requests will have those +// requests sampled uniformly as well, but if requests are bursty, especially sub-second, then a +// number of sequential requests can be sampled each second. +type rateLimitingSampler struct { + maxTracesPerSecond float64 + rateLimiter *utils.RateLimiter +} + +// newRateLimitingSampler creates new rateLimitingSampler. +func newRateLimitingSampler(maxTracesPerSecond float64) *rateLimitingSampler { + s := new(rateLimitingSampler) + return s.init(maxTracesPerSecond) +} + +func (s *rateLimitingSampler) init(maxTracesPerSecond float64) *rateLimitingSampler { + if s.rateLimiter == nil { + s.rateLimiter = utils.NewRateLimiter(maxTracesPerSecond, math.Max(maxTracesPerSecond, 1.0)) + } else { + s.rateLimiter.Update(maxTracesPerSecond, math.Max(maxTracesPerSecond, 1.0)) + } + s.maxTracesPerSecond = maxTracesPerSecond + return s +} + +func (s *rateLimitingSampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult { + psc := oteltrace.SpanContextFromContext(p.ParentContext) + if s.rateLimiter.CheckCredit(1.0) { + return trace.SamplingResult{ + Decision: trace.RecordAndSample, + Tracestate: psc.TraceState(), + } + } + return trace.SamplingResult{ + Decision: trace.Drop, + Tracestate: psc.TraceState(), + } +} + +// Update reconfigures the rate limiter, while preserving its accumulated balance. +// Locking must be done externally. +func (s *rateLimitingSampler) Update(maxTracesPerSecond float64) { + if s.maxTracesPerSecond != maxTracesPerSecond { + s.init(maxTracesPerSecond) + } +} + +// Equal compares with another sampler. +func (s *rateLimitingSampler) Equal(other trace.Sampler) bool { + if o, ok := other.(*rateLimitingSampler); ok { + return s.maxTracesPerSecond == o.maxTracesPerSecond + } + return false +} + +func (s *rateLimitingSampler) Description() string { + return "rateLimitingSampler{}" +} + +// ----------------------- + +// guaranteedThroughputProbabilisticSampler is a sampler that leverages both probabilisticSampler and +// rateLimitingSampler. The rateLimitingSampler is used as a guaranteed lower bound sampler such that +// every operation is sampled at least once in a time interval defined by the lowerBound. ie a lowerBound +// of 1.0 / (60 * 10) will sample an operation at least once every 10 minutes. +// +// The probabilisticSampler is given higher priority when tags are emitted, ie. if IsSampled() for both +// samplers return true, the tags for probabilisticSampler will be used. +type guaranteedThroughputProbabilisticSampler struct { + probabilisticSampler *probabilisticSampler + lowerBoundSampler *rateLimitingSampler + samplingRate float64 + lowerBound float64 +} + +func newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate float64) *guaranteedThroughputProbabilisticSampler { + s := &guaranteedThroughputProbabilisticSampler{ + lowerBoundSampler: newRateLimitingSampler(lowerBound), + lowerBound: lowerBound, + } + s.setProbabilisticSampler(samplingRate) + return s +} + +func (s *guaranteedThroughputProbabilisticSampler) setProbabilisticSampler(samplingRate float64) { + if s.probabilisticSampler == nil { + s.probabilisticSampler = newProbabilisticSampler(samplingRate) + } else if s.samplingRate != samplingRate { + s.probabilisticSampler.init(samplingRate) + } + // since we don't validate samplingRate, sampler may have clamped it to [0, 1] interval + s.samplingRate = s.probabilisticSampler.SamplingRate() +} + +func (s *guaranteedThroughputProbabilisticSampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult { + if result := s.probabilisticSampler.ShouldSample(p); result.Decision == trace.RecordAndSample { + s.lowerBoundSampler.ShouldSample(p) + return result + } + result := s.lowerBoundSampler.ShouldSample(p) + return result +} + +// this function should only be called while holding a Write lock +func (s *guaranteedThroughputProbabilisticSampler) update(lowerBound, samplingRate float64) { + s.setProbabilisticSampler(samplingRate) + if s.lowerBound != lowerBound { + s.lowerBoundSampler.Update(lowerBound) + s.lowerBound = lowerBound + } +} + +func (s *guaranteedThroughputProbabilisticSampler) Description() string { + return "guaranteedThroughputProbabilisticSampler{}" +} + +// ----------------------- + +// perOperationSampler is a delegating sampler that applies guaranteedThroughputProbabilisticSampler +// on a per-operation basis. +type perOperationSampler struct { + sync.RWMutex + + samplers map[string]*guaranteedThroughputProbabilisticSampler + defaultSampler *probabilisticSampler + lowerBound float64 + maxOperations int + + // see description in perOperationSamplerParams + operationNameLateBinding bool +} + +// perOperationSamplerParams defines parameters when creating perOperationSampler. +type perOperationSamplerParams struct { + // Max number of operations that will be tracked. Other operations will be given default strategy. + MaxOperations int + + // Opt-in feature for applications that require late binding of span name via explicit call to SetOperationName. + // When this feature is enabled, the sampler will return retryable=true from OnCreateSpan(), thus leaving + // the sampling decision as non-final (and the span as writeable). This may lead to degraded performance + // in applications that always provide the correct span name on oteltrace creation. + // + // For backwards compatibility this option is off by default. + OperationNameLateBinding bool + + // Initial configuration of the sampling strategies (usually retrieved from the backend by Remote Sampler). + Strategies *jaeger_api_v2.PerOperationSamplingStrategies +} + +// newPerOperationSampler returns a new perOperationSampler. +func newPerOperationSampler(params perOperationSamplerParams) *perOperationSampler { + if params.MaxOperations <= 0 { + params.MaxOperations = defaultMaxOperations + } + samplers := make(map[string]*guaranteedThroughputProbabilisticSampler) + for _, strategy := range params.Strategies.PerOperationStrategies { + sampler := newGuaranteedThroughputProbabilisticSampler( + params.Strategies.DefaultLowerBoundTracesPerSecond, + strategy.ProbabilisticSampling.SamplingRate, + ) + samplers[strategy.Operation] = sampler + } + return &perOperationSampler{ + samplers: samplers, + defaultSampler: newProbabilisticSampler(params.Strategies.DefaultSamplingProbability), + lowerBound: params.Strategies.DefaultLowerBoundTracesPerSecond, + maxOperations: params.MaxOperations, + operationNameLateBinding: params.OperationNameLateBinding, + } +} + +func (s *perOperationSampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult { + sampler := s.getSamplerForOperation(p.Name) + return sampler.ShouldSample(p) +} + +func (s *perOperationSampler) getSamplerForOperation(operation string) trace.Sampler { + s.RLock() + sampler, ok := s.samplers[operation] + if ok { + defer s.RUnlock() + return sampler + } + s.RUnlock() + s.Lock() + defer s.Unlock() + + // Check if sampler has already been created + sampler, ok = s.samplers[operation] + if ok { + return sampler + } + // Store only up to maxOperations of unique ops. + if len(s.samplers) >= s.maxOperations { + return s.defaultSampler + } + newSampler := newGuaranteedThroughputProbabilisticSampler(s.lowerBound, s.defaultSampler.SamplingRate()) + s.samplers[operation] = newSampler + return newSampler +} + +func (s *perOperationSampler) Description() string { + return "perOperationSampler{}" +} + +func (s *perOperationSampler) update(strategies *jaeger_api_v2.PerOperationSamplingStrategies) { + s.Lock() + defer s.Unlock() + newSamplers := map[string]*guaranteedThroughputProbabilisticSampler{} + for _, strategy := range strategies.PerOperationStrategies { + operation := strategy.Operation + samplingRate := strategy.ProbabilisticSampling.SamplingRate + lowerBound := strategies.DefaultLowerBoundTracesPerSecond + if sampler, ok := s.samplers[operation]; ok { + sampler.update(lowerBound, samplingRate) + newSamplers[operation] = sampler + } else { + sampler := newGuaranteedThroughputProbabilisticSampler( + lowerBound, + samplingRate, + ) + newSamplers[operation] = sampler + } + } + s.lowerBound = strategies.DefaultLowerBoundTracesPerSecond + if s.defaultSampler.SamplingRate() != strategies.DefaultSamplingProbability { + s.defaultSampler = newProbabilisticSampler(strategies.DefaultSamplingProbability) + } + s.samplers = newSamplers +} diff --git a/samplers/jaegerremote/sampler_remote.go b/samplers/jaegerremote/sampler_remote.go new file mode 100644 index 00000000000..5261127ffa3 --- /dev/null +++ b/samplers/jaegerremote/sampler_remote.go @@ -0,0 +1,314 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "sync" + "sync/atomic" + "time" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + defaultRemoteSamplingTimeout = 10 * time.Second + defaultSamplingRefreshInterval = time.Minute + defaultSamplingMaxOperations = 256 + defaultSamplingOperationNameLateBinding = true +) + +// samplingStrategyFetcher is used to fetch sampling strategy updates from remote server. +type samplingStrategyFetcher interface { + Fetch(service string) ([]byte, error) +} + +// samplingStrategyParser is used to parse sampling strategy updates. The output object +// should be of the type that is recognized by the SamplerUpdaters. +type samplingStrategyParser interface { + Parse(response []byte) (interface{}, error) +} + +// samplerUpdater is used by Sampler to apply sampling strategies, +// retrieved from remote config server, to the current sampler. The updater can modify +// the sampler in-place if sampler supports it, or create a new one. +// +// If the strategy does not contain configuration for the sampler in question, +// updater must return modifiedSampler=nil to give other updaters a chance to inspect +// the sampling strategy response. +// +// Sampler invokes the updaters while holding a lock on the main sampler. +type samplerUpdater interface { + Update(sampler trace.Sampler, strategy interface{}) (modified trace.Sampler, err error) +} + +// Sampler is a delegating sampler that polls a remote server +// for the appropriate sampling strategy, constructs a corresponding sampler and +// delegates to it for sampling decisions. +type Sampler struct { + // These fields must be first in the struct because `sync/atomic` expects 64-bit alignment. + // Cf. https://github.com/uber/jaeger-client-go/issues/155, https://goo.gl/zW7dgq + closed int64 // 0 - not closed, 1 - closed + + sync.RWMutex // used to serialize access to samplerConfig.sampler + config + + serviceName string + doneChan chan *sync.WaitGroup +} + +// New creates a sampler that periodically pulls +// the sampling strategy from an HTTP sampling server (e.g. jaeger-agent). +func New( + serviceName string, + opts ...Option, +) *Sampler { + options := newConfig(opts...) + sampler := &Sampler{ + config: options, + serviceName: serviceName, + doneChan: make(chan *sync.WaitGroup), + } + go sampler.pollController() + return sampler +} + +func (s *Sampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult { + s.RLock() + defer s.RUnlock() + return s.sampler.ShouldSample(p) +} + +// Close does a clean shutdown of the sampler, stopping any background +// go-routines it may have started. +func (s *Sampler) Close() { + if swapped := atomic.CompareAndSwapInt64(&s.closed, 0, 1); !swapped { + otel.Handle(fmt.Errorf("repeated attempt to close the sampler is ignored")) + return + } + + var wg sync.WaitGroup + wg.Add(1) + s.doneChan <- &wg + wg.Wait() +} + +func (s *Sampler) Description() string { + return "JaegerRemoteSampler{}" +} + +func (s *Sampler) pollController() { + ticker := time.NewTicker(s.samplingRefreshInterval) + defer ticker.Stop() + s.pollControllerWithTicker(ticker) +} + +func (s *Sampler) pollControllerWithTicker(ticker *time.Ticker) { + for { + select { + case <-ticker.C: + s.UpdateSampler() + case wg := <-s.doneChan: + wg.Done() + return + } + } +} + +func (s *Sampler) setSampler(sampler trace.Sampler) { + s.Lock() + defer s.Unlock() + s.sampler = sampler +} + +// UpdateSampler forces the sampler to fetch sampling strategy from backend server. +// This function is called automatically on a timer, but can also be safely called manually, e.g. from tests. +func (s *Sampler) UpdateSampler() { + res, err := s.samplingFetcher.Fetch(s.serviceName) + if err != nil { + // log.Printf("failed to fetch sampling strategy: %v", err) + return + } + strategy, err := s.samplingParser.Parse(res) + if err != nil { + // log.Printf("failed to parse sampling strategy response: %v", err) + return + } + + s.Lock() + defer s.Unlock() + + if err := s.updateSamplerViaUpdaters(strategy); err != nil { + //c.logger.Infof("failed to handle sampling strategy response %+v. Got error: %v", res, err) + return + } +} + +// NB: this function should only be called while holding a Write lock +func (s *Sampler) updateSamplerViaUpdaters(strategy interface{}) error { + for _, updater := range s.updaters { + sampler, err := updater.Update(s.sampler, strategy) + if err != nil { + return err + } + if sampler != nil { + s.sampler = sampler + return nil + } + } + return fmt.Errorf("unsupported sampling strategy %+v", strategy) +} + +// ----------------------- + +// probabilisticSamplerUpdater is used by Sampler to parse sampling configuration. +type probabilisticSamplerUpdater struct{} + +// Update implements Update of samplerUpdater. +func (u *probabilisticSamplerUpdater) Update(sampler trace.Sampler, strategy interface{}) (trace.Sampler, error) { + type response interface { + GetProbabilisticSampling() *jaeger_api_v2.ProbabilisticSamplingStrategy + } + var _ response = new(jaeger_api_v2.SamplingStrategyResponse) // sanity signature check + if resp, ok := strategy.(response); ok { + if probabilistic := resp.GetProbabilisticSampling(); probabilistic != nil { + if ps, ok := sampler.(*probabilisticSampler); ok { + if err := ps.Update(probabilistic.SamplingRate); err != nil { + return nil, err + } + return sampler, nil + } + return newProbabilisticSampler(probabilistic.SamplingRate), nil + } + } + return nil, nil +} + +// ----------------------- + +// rateLimitingSamplerUpdater is used by Sampler to parse sampling configuration. +type rateLimitingSamplerUpdater struct{} + +// Update implements Update of samplerUpdater. +func (u *rateLimitingSamplerUpdater) Update(sampler trace.Sampler, strategy interface{}) (trace.Sampler, error) { + type response interface { + GetRateLimitingSampling() *jaeger_api_v2.RateLimitingSamplingStrategy + } + var _ response = new(jaeger_api_v2.SamplingStrategyResponse) // sanity signature check + if resp, ok := strategy.(response); ok { + if rateLimiting := resp.GetRateLimitingSampling(); rateLimiting != nil { + rateLimit := float64(rateLimiting.MaxTracesPerSecond) + if rl, ok := sampler.(*rateLimitingSampler); ok { + rl.Update(rateLimit) + return rl, nil + } + return newRateLimitingSampler(rateLimit), nil + } + } + return nil, nil +} + +// ----------------------- + +// perOperationSamplerUpdater is used by Sampler to parse sampling configuration. +// Fields have the same meaning as in perOperationSamplerParams. +type perOperationSamplerUpdater struct { + MaxOperations int + OperationNameLateBinding bool +} + +// Update implements Update of samplerUpdater. +func (u *perOperationSamplerUpdater) Update(sampler trace.Sampler, strategy interface{}) (trace.Sampler, error) { + type response interface { + GetOperationSampling() *jaeger_api_v2.PerOperationSamplingStrategies + } + var _ response = new(jaeger_api_v2.SamplingStrategyResponse) // sanity signature check + if p, ok := strategy.(response); ok { + if operations := p.GetOperationSampling(); operations != nil { + if as, ok := sampler.(*perOperationSampler); ok { + as.update(operations) + return as, nil + } + return newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: u.MaxOperations, + OperationNameLateBinding: u.OperationNameLateBinding, + Strategies: operations, + }), nil + } + } + return nil, nil +} + +// ----------------------- + +type httpSamplingStrategyFetcher struct { + serverURL string + httpClient http.Client +} + +func newHTTPSamplingStrategyFetcher(serverURL string) *httpSamplingStrategyFetcher { + customTransport := http.DefaultTransport.(*http.Transport).Clone() + customTransport.ResponseHeaderTimeout = defaultRemoteSamplingTimeout + + return &httpSamplingStrategyFetcher{ + serverURL: serverURL, + httpClient: http.Client{ + Transport: customTransport, + }, + } +} + +func (f *httpSamplingStrategyFetcher) Fetch(serviceName string) ([]byte, error) { + v := url.Values{} + v.Set("service", serviceName) + uri := f.serverURL + "?" + v.Encode() + + resp, err := f.httpClient.Get(uri) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("StatusCode: %d, Body: %c", resp.StatusCode, body) + } + + return body, nil +} + +// ----------------------- + +type samplingStrategyParserImpl struct{} + +func (p *samplingStrategyParserImpl) Parse(response []byte) (interface{}, error) { + strategy := new(jaeger_api_v2.SamplingStrategyResponse) + if err := json.Unmarshal(response, strategy); err != nil { + return nil, err + } + return strategy, nil +} diff --git a/samplers/jaegerremote/sampler_remote_options.go b/samplers/jaegerremote/sampler_remote_options.go new file mode 100644 index 00000000000..3e0394a423f --- /dev/null +++ b/samplers/jaegerremote/sampler_remote_options.go @@ -0,0 +1,135 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "time" + + "go.opentelemetry.io/otel/sdk/trace" +) + +type config struct { + sampler trace.Sampler + samplingServerURL string + samplingRefreshInterval time.Duration + samplingFetcher samplingStrategyFetcher + samplingParser samplingStrategyParser + updaters []samplerUpdater + posParams perOperationSamplerParams +} + +// newConfig returns an appropriately configured config. +func newConfig(options ...Option) config { + c := config{ + sampler: newProbabilisticSampler(0.001), + samplingServerURL: defaultSamplingServerURL, + samplingRefreshInterval: defaultSamplingRefreshInterval, + samplingFetcher: newHTTPSamplingStrategyFetcher(defaultSamplingServerURL), + samplingParser: new(samplingStrategyParserImpl), + updaters: []samplerUpdater{ + new(probabilisticSamplerUpdater), + new(rateLimitingSamplerUpdater), + }, + posParams: perOperationSamplerParams{ + MaxOperations: defaultSamplingMaxOperations, + OperationNameLateBinding: defaultSamplingOperationNameLateBinding, + }, + } + for _, option := range options { + option.apply(&c) + } + c.updaters = append(c.updaters, + &perOperationSamplerUpdater{ + MaxOperations: c.posParams.MaxOperations, + OperationNameLateBinding: c.posParams.OperationNameLateBinding, + }) + + return c +} + +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (fn optionFunc) apply(c *config) { + fn(c) +} + +// WithInitialSampler creates a Option that sets the initial sampler +// to use before a remote sampler is created and used. +func WithInitialSampler(sampler trace.Sampler) Option { + return optionFunc(func(c *config) { + c.sampler = sampler + }) +} + +// WithSamplingServerURL creates a Option that sets the sampling server url +// of the local agent that contains the sampling strategies. +func WithSamplingServerURL(samplingServerURL string) Option { + return optionFunc(func(c *config) { + c.samplingServerURL = samplingServerURL + // The default port of jaeger agent is 5778, but there are other ports specified by the user, so the sampling address and fetch address are strongly bound + c.samplingFetcher = newHTTPSamplingStrategyFetcher(samplingServerURL) + }) +} + +// WithMaxOperations creates a Option that sets the maximum number of +// operations the sampler will keep track of. +func WithMaxOperations(maxOperations int) Option { + return optionFunc(func(c *config) { + c.posParams.MaxOperations = maxOperations + }) +} + +// WithOperationNameLateBinding creates a Option that sets the respective +// field in the perOperationSamplerParams. +func WithOperationNameLateBinding(enable bool) Option { + return optionFunc(func(c *config) { + c.posParams.OperationNameLateBinding = enable + }) +} + +// WithSamplingRefreshInterval creates a Option that sets how often the +// sampler will poll local agent for the appropriate sampling strategy. +func WithSamplingRefreshInterval(samplingRefreshInterval time.Duration) Option { + return optionFunc(func(c *config) { + c.samplingRefreshInterval = samplingRefreshInterval + }) +} + +// samplingStrategyFetcher creates a Option that initializes sampling strategy fetcher. +func withSamplingStrategyFetcher(fetcher samplingStrategyFetcher) Option { + return optionFunc(func(c *config) { + c.samplingFetcher = fetcher + }) +} + +// samplingStrategyParser creates a Option that initializes sampling strategy parser. +func withSamplingStrategyParser(parser samplingStrategyParser) Option { + return optionFunc(func(c *config) { + c.samplingParser = parser + }) +} + +// withUpdaters creates a Option that initializes sampler updaters. +func withUpdaters(updaters ...samplerUpdater) Option { + return optionFunc(func(c *config) { + c.updaters = updaters + }) +} diff --git a/samplers/jaegerremote/sampler_remote_test.go b/samplers/jaegerremote/sampler_remote_test.go new file mode 100644 index 00000000000..4fe4e8bee7c --- /dev/null +++ b/samplers/jaegerremote/sampler_remote_test.go @@ -0,0 +1,465 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "encoding/binary" + "errors" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" + "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/testutils" + "go.opentelemetry.io/otel/sdk/trace" + oteltrace "go.opentelemetry.io/otel/trace" +) + +func TestRemotelyControlledSampler_updateRace(t *testing.T) { + initSampler := newProbabilisticSampler(0.123) + fetcher := &testSamplingStrategyFetcher{response: []byte("probabilistic")} + parser := new(testSamplingStrategyParser) + updaters := []samplerUpdater{new(probabilisticSamplerUpdater)} + sampler := New( + "test", + WithMaxOperations(42), + WithOperationNameLateBinding(true), + WithInitialSampler(initSampler), + WithSamplingServerURL("my url"), + WithSamplingRefreshInterval(time.Millisecond), + withSamplingStrategyFetcher(fetcher), + withSamplingStrategyParser(parser), + withUpdaters(updaters...), + ) + + s := makeSamplingParameters(1, "test") + end := make(chan struct{}) + + accessor := func(f func()) { + for { + select { + case <-end: + return + default: + f() + } + } + } + + go accessor(func() { + sampler.UpdateSampler() + }) + + go accessor(func() { + sampler.ShouldSample(s) + }) + + time.Sleep(100 * time.Millisecond) + close(end) + sampler.Close() +} + +type testSamplingStrategyFetcher struct { + response []byte +} + +func (c *testSamplingStrategyFetcher) Fetch(serviceName string) ([]byte, error) { + return c.response, nil +} + +type testSamplingStrategyParser struct { +} + +func (p *testSamplingStrategyParser) Parse(response []byte) (interface{}, error) { + strategy := new(jaeger_api_v2.SamplingStrategyResponse) + + switch string(response) { + case "probabilistic": + strategy.StrategyType = jaeger_api_v2.SamplingStrategyType_PROBABILISTIC + strategy.ProbabilisticSampling = &jaeger_api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 0.85, + } + return strategy, nil + } + + return nil, errors.New("unknown strategy test request") +} + +func TestRemoteSamplerOptions(t *testing.T) { + initSampler := newProbabilisticSampler(0.123) + fetcher := new(fakeSamplingFetcher) + parser := new(samplingStrategyParserImpl) + updaters := []samplerUpdater{new(probabilisticSamplerUpdater)} + sampler := New( + "test", + WithMaxOperations(42), + WithOperationNameLateBinding(true), + WithInitialSampler(initSampler), + WithSamplingServerURL("my url"), + WithSamplingRefreshInterval(42*time.Second), + withSamplingStrategyFetcher(fetcher), + withSamplingStrategyParser(parser), + withUpdaters(updaters...), + ) + assert.Equal(t, 42, sampler.posParams.MaxOperations) + assert.True(t, sampler.posParams.OperationNameLateBinding) + assert.Same(t, initSampler, sampler.sampler) + assert.Equal(t, "my url", sampler.samplingServerURL) + assert.Equal(t, 42*time.Second, sampler.samplingRefreshInterval) + assert.Same(t, fetcher, sampler.samplingFetcher) + assert.Same(t, parser, sampler.samplingParser) + assert.Same(t, updaters[0], sampler.updaters[0]) +} + +func TestRemoteSamplerOptionsDefaults(t *testing.T) { + options := newConfig() + sampler, ok := options.sampler.(*probabilisticSampler) + assert.True(t, ok) + assert.Equal(t, 0.001, sampler.samplingRate) + + assert.NotEmpty(t, options.samplingServerURL) + assert.NotZero(t, options.samplingRefreshInterval) +} + +func initAgent(t *testing.T) (*testutils.MockAgent, *Sampler) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + + initialSampler := newProbabilisticSampler(0.001) + sampler := New( + "client app", + WithSamplingServerURL("http://"+agent.SamplingServerAddr()), + WithMaxOperations(testDefaultMaxOperations), + WithInitialSampler(initialSampler), + WithSamplingRefreshInterval(time.Minute), + ) + sampler.Close() // stop timer-based updates, we want to call them manually + + return agent, sampler +} + +func makeSamplingParameters(id uint64, operationName string) trace.SamplingParameters { + var traceID oteltrace.TraceID + binary.BigEndian.PutUint64(traceID[:], id) + + return trace.SamplingParameters{ + TraceID: traceID, + Name: operationName, + } +} + +func TestRemotelyControlledSampler(t *testing.T) { + agent, remoteSampler := initAgent(t) + defer agent.Close() + + defaultSampler := newProbabilisticSampler(0.001) + remoteSampler.setSampler(defaultSampler) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, testDefaultSamplingProbability)) + remoteSampler.UpdateSampler() + s1, ok := remoteSampler.sampler.(*probabilisticSampler) + assert.True(t, ok) + assert.EqualValues(t, testDefaultSamplingProbability, s1.samplingRate, "Sampler should have been updated") + + result := remoteSampler.ShouldSample(makeSamplingParameters(testMaxID+10, testOperationName)) + assert.Equal(t, trace.Drop, result.Decision) + result = remoteSampler.ShouldSample(makeSamplingParameters(testMaxID-10, testOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) + + remoteSampler.setSampler(defaultSampler) + + c := make(chan time.Time) + ticker := &time.Ticker{C: c} + // reset closed so the next call to Close() correctly stops the polling goroutine + remoteSampler.closed = 0 + go remoteSampler.pollControllerWithTicker(ticker) + + c <- time.Now() // force update based on timer + time.Sleep(10 * time.Millisecond) + remoteSampler.Close() + + s2, ok := remoteSampler.sampler.(*probabilisticSampler) + assert.True(t, ok) + assert.EqualValues(t, testDefaultSamplingProbability, s2.samplingRate, "Sampler should have been updated from timer") +} + +func TestRemotelyControlledSampler_updateSampler(t *testing.T) { + tests := []struct { + probabilities map[string]float64 + defaultProbability float64 + expectedDefaultProbability float64 + }{ + { + probabilities: map[string]float64{testOperationName: 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + }, + { + probabilities: map[string]float64{testOperationName: testDefaultSamplingProbability}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + }, + { + probabilities: map[string]float64{ + testOperationName: testDefaultSamplingProbability, + testFirstTimeOperationName: testDefaultSamplingProbability, + }, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: 1.1, + expectedDefaultProbability: 1.0, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + agent, sampler := initAgent(t) + defer agent.Close() + + initSampler, ok := sampler.sampler.(*probabilisticSampler) + assert.True(t, ok) + + res := &jaeger_api_v2.SamplingStrategyResponse{ + StrategyType: jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, + OperationSampling: &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: test.defaultProbability, + DefaultLowerBoundTracesPerSecond: 0.001, + }, + } + for opName, prob := range test.probabilities { + res.OperationSampling.PerOperationStrategies = append(res.OperationSampling.PerOperationStrategies, + &jaeger_api_v2.OperationSamplingStrategy{ + Operation: opName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: prob, + }, + }, + ) + } + + agent.AddSamplingStrategy("client app", res) + sampler.UpdateSampler() + + s, ok := sampler.sampler.(*perOperationSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, sampler.sampler, "Sampler should have been updated") + assert.Equal(t, test.expectedDefaultProbability, s.defaultSampler.SamplingRate()) + + // First call is always sampled + result := sampler.ShouldSample(makeSamplingParameters(testMaxID+10, testOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) + + result = sampler.ShouldSample(makeSamplingParameters(testMaxID-10, testOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) + }) + } +} + +func TestSamplerQueryError(t *testing.T) { + agent, sampler := initAgent(t) + defer agent.Close() + + // override the actual handler + sampler.samplingFetcher = &fakeSamplingFetcher{} + + initSampler, ok := sampler.sampler.(*probabilisticSampler) + assert.True(t, ok) + + sampler.Close() // stop timer-based updates, we want to call them manually + + sampler.UpdateSampler() + assert.Equal(t, initSampler, sampler.sampler, "Sampler should not have been updated due to query error") +} + +type fakeSamplingFetcher struct{} + +func (c *fakeSamplingFetcher) Fetch(serviceName string) ([]byte, error) { + return nil, errors.New("query error") +} + +func TestRemotelyControlledSampler_updateSamplerFromAdaptiveSampler(t *testing.T) { + agent, remoteSampler := initAgent(t) + defer agent.Close() + remoteSampler.Close() // close the second time (initAgent already called Close) + + strategies := &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + } + adaptiveSampler := newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: testDefaultMaxOperations, + Strategies: strategies, + }) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.setSampler(adaptiveSampler) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, 0.5)) + remoteSampler.UpdateSampler() + + // Sampler should have been updated to probabilistic + _, ok := remoteSampler.sampler.(*probabilisticSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.setSampler(adaptiveSampler) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, 1)) + remoteSampler.UpdateSampler() + + // Sampler should have been updated to ratelimiting + _, ok = remoteSampler.sampler.(*rateLimitingSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.setSampler(adaptiveSampler) + + // Update existing adaptive sampler + agent.AddSamplingStrategy("client app", &jaeger_api_v2.SamplingStrategyResponse{OperationSampling: strategies}) + remoteSampler.UpdateSampler() +} + +func TestRemotelyControlledSampler_updateRateLimitingOrProbabilisticSampler(t *testing.T) { + probabilisticSampler := newProbabilisticSampler(0.002) + otherProbabilisticSampler := newProbabilisticSampler(0.003) + maxProbabilisticSampler := newProbabilisticSampler(1.0) + + rateLimitingSampler := newRateLimitingSampler(2) + otherRateLimitingSampler := newRateLimitingSampler(3) + + testCases := []struct { + res *jaeger_api_v2.SamplingStrategyResponse + initSampler trace.Sampler + expectedSampler trace.Sampler + shouldErr bool + referenceEquivalence bool + caption string + }{ + { + res: getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, 1.5), + initSampler: probabilisticSampler, + expectedSampler: maxProbabilisticSampler, + shouldErr: true, + referenceEquivalence: false, + caption: "invalid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, 0.002), + initSampler: probabilisticSampler, + expectedSampler: probabilisticSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, 0.003), + initSampler: probabilisticSampler, + expectedSampler: otherProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, 2), + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged rate limiting strategy", + }, + { + res: getSamplingStrategyResponse(jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, 3), + initSampler: rateLimitingSampler, + expectedSampler: otherRateLimitingSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid rate limiting strategy", + }, + { + res: &jaeger_api_v2.SamplingStrategyResponse{}, + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: true, + referenceEquivalence: true, + caption: "invalid strategy", + }, + } + + for _, tc := range testCases { + testCase := tc // capture loop var + t.Run(testCase.caption, func(t *testing.T) { + remoteSampler := New( + "test", + WithInitialSampler(testCase.initSampler), + withUpdaters( + new(probabilisticSamplerUpdater), + new(rateLimitingSamplerUpdater), + ), + ) + err := remoteSampler.updateSamplerViaUpdaters(testCase.res) + if testCase.shouldErr { + require.Error(t, err) + return + } + if testCase.referenceEquivalence { + assert.Equal(t, testCase.expectedSampler, remoteSampler.sampler) + } else { + type comparable interface { + Equal(other trace.Sampler) bool + } + es, esOk := testCase.expectedSampler.(comparable) + require.True(t, esOk, "expected sampler %+v must implement Equal()", testCase.expectedSampler) + assert.True(t, es.Equal(remoteSampler.sampler), + "sampler.Equal: want=%+v, have=%+v", testCase.expectedSampler, remoteSampler.sampler) + } + }) + } +} + +func getSamplingStrategyResponse(strategyType jaeger_api_v2.SamplingStrategyType, value float64) *jaeger_api_v2.SamplingStrategyResponse { + if strategyType == jaeger_api_v2.SamplingStrategyType_PROBABILISTIC { + return &jaeger_api_v2.SamplingStrategyResponse{ + StrategyType: jaeger_api_v2.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: value, + }, + } + } + if strategyType == jaeger_api_v2.SamplingStrategyType_RATE_LIMITING { + return &jaeger_api_v2.SamplingStrategyResponse{ + StrategyType: jaeger_api_v2.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &jaeger_api_v2.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int32(value), + }, + } + } + return nil +} diff --git a/samplers/jaegerremote/sampler_test.go b/samplers/jaegerremote/sampler_test.go new file mode 100644 index 00000000000..71096118477 --- /dev/null +++ b/samplers/jaegerremote/sampler_test.go @@ -0,0 +1,231 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2021 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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 jaegerremote + +import ( + "encoding/binary" + "testing" + + "github.com/stretchr/testify/assert" + + jaeger_api_v2 "go.opentelemetry.io/contrib/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2" + "go.opentelemetry.io/otel/sdk/trace" + oteltrace "go.opentelemetry.io/otel/trace" +) + +const ( + testOperationName = "op" + testFirstTimeOperationName = "firstTimeOp" + + testDefaultSamplingProbability = 0.5 + testMaxID = uint64(1) << 62 + testDefaultMaxOperations = 10 +) + +func TestProbabilisticSampler(t *testing.T) { + var traceID oteltrace.TraceID + + sampler := newProbabilisticSampler(0.5) + binary.BigEndian.PutUint64(traceID[:], testMaxID+10) + result := sampler.ShouldSample(trace.SamplingParameters{TraceID: traceID}) + assert.Equal(t, trace.Drop, result.Decision) + binary.BigEndian.PutUint64(traceID[:], testMaxID-20) + result = sampler.ShouldSample(trace.SamplingParameters{TraceID: traceID}) + assert.Equal(t, trace.RecordAndSample, result.Decision) + + t.Run("test_64bit_id", func(t *testing.T) { + binary.BigEndian.PutUint64(traceID[:], (testMaxID+10)|1<<63) + result = sampler.ShouldSample(trace.SamplingParameters{TraceID: traceID}) + assert.Equal(t, trace.Drop, result.Decision) + binary.BigEndian.PutUint64(traceID[:], (testMaxID-20)|1<<63) + result = sampler.ShouldSample(trace.SamplingParameters{TraceID: traceID}) + assert.Equal(t, trace.RecordAndSample, result.Decision) + }) +} + +func TestRateLimitingSampler(t *testing.T) { + sampler := newRateLimitingSampler(2) + result := sampler.ShouldSample(trace.SamplingParameters{Name: testOperationName}) + assert.Equal(t, trace.RecordAndSample, result.Decision) + result = sampler.ShouldSample(trace.SamplingParameters{Name: testOperationName}) + assert.Equal(t, trace.RecordAndSample, result.Decision) + result = sampler.ShouldSample(trace.SamplingParameters{Name: testOperationName}) + assert.Equal(t, trace.Drop, result.Decision) + + sampler = newRateLimitingSampler(0.1) + result = sampler.ShouldSample(trace.SamplingParameters{Name: testOperationName}) + assert.Equal(t, trace.RecordAndSample, result.Decision) + result = sampler.ShouldSample(trace.SamplingParameters{Name: testOperationName}) + assert.Equal(t, trace.Drop, result.Decision) +} + +func TestGuaranteedThroughputProbabilisticSamplerUpdate(t *testing.T) { + samplingRate := 0.5 + lowerBound := 2.0 + sampler := newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate) + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, samplingRate, sampler.samplingRate) + + newSamplingRate := 0.6 + newLowerBound := 1.0 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newSamplingRate, sampler.samplingRate) + + newSamplingRate = 1.1 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, 1.0, sampler.samplingRate) +} + +func TestAdaptiveSampler(t *testing.T) { + samplingRates := []*jaeger_api_v2.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: testDefaultSamplingProbability}, + }, + } + strategies := &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + PerOperationStrategies: samplingRates, + } + + sampler := newPerOperationSampler(perOperationSamplerParams{ + Strategies: strategies, + MaxOperations: 42, + }) + assert.Equal(t, 42, sampler.maxOperations) + + sampler = newPerOperationSampler(perOperationSamplerParams{Strategies: strategies}) + assert.Equal(t, sampler.maxOperations, 2000, "default MaxOperations applied") + + sampler = newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: testDefaultMaxOperations, + Strategies: strategies, + }) + + result := sampler.ShouldSample(makeSamplingParameters(testMaxID+10, testOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) + + result = sampler.ShouldSample(makeSamplingParameters(testMaxID-20, testOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) + + result = sampler.ShouldSample(makeSamplingParameters(testMaxID+10, testOperationName)) + assert.Equal(t, trace.Drop, result.Decision) + + // This operation is seen for the first time by the sampler + result = sampler.ShouldSample(makeSamplingParameters(testMaxID, testFirstTimeOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) +} + +func TestAdaptiveSamplerErrors(t *testing.T) { + strategies := &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: []*jaeger_api_v2.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: -0.1}, + }, + }, + } + + sampler := newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: testDefaultMaxOperations, + Strategies: strategies, + }) + assert.Equal(t, 0.0, sampler.samplers[testOperationName].samplingRate) + + strategies.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate = 1.1 + sampler = newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: testDefaultMaxOperations, + Strategies: strategies, + }) + assert.Equal(t, 1.0, sampler.samplers[testOperationName].samplingRate) +} + +func TestAdaptiveSamplerUpdate(t *testing.T) { + samplingRate := 0.1 + lowerBound := 2.0 + samplingRates := []*jaeger_api_v2.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: samplingRate}, + }, + } + strategies := &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: lowerBound, + PerOperationStrategies: samplingRates, + } + + sampler := newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: testDefaultMaxOperations, + Strategies: strategies, + }) + + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, testDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 1) + + // Update the sampler with new sampling rates + newSamplingRate := 0.2 + newLowerBound := 3.0 + newDefaultSamplingProbability := 0.1 + newSamplingRates := []*jaeger_api_v2.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + { + Operation: testFirstTimeOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + } + strategies = &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: newDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: newLowerBound, + PerOperationStrategies: newSamplingRates, + } + + sampler.update(strategies) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 2) +} + +func TestMaxOperations(t *testing.T) { + samplingRates := []*jaeger_api_v2.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &jaeger_api_v2.ProbabilisticSamplingStrategy{SamplingRate: 0.1}, + }, + } + strategies := &jaeger_api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: samplingRates, + } + + sampler := newPerOperationSampler(perOperationSamplerParams{ + MaxOperations: 1, + Strategies: strategies, + }) + + result := sampler.ShouldSample(makeSamplingParameters(testMaxID-10, testFirstTimeOperationName)) + assert.Equal(t, trace.RecordAndSample, result.Decision) +}