From 233832f8f136e434d442dc144bb908010a5d41be Mon Sep 17 00:00:00 2001 From: Kristina Pathak Date: Fri, 9 Dec 2022 09:47:03 -0800 Subject: [PATCH] upgrade otel version to v1.11.2 (#333) * wip, progress so far * more similar structure to otel, fixing imports * fix for attributesEqual() following otel-go slice-valued attribute representation change, see https://github.com/open-telemetry/opentelemetry-go/issues/3108 * more metric->view pkg fixes * remove otlpconfig pkg * export NewClient() func!! * moved some things around * fix exporter, pipeline imports * go.mod updates * fix shutdown deadlock * upgrade versions to v1.11.2 and v0.34.0 * fix example I missed * work on instrumentation unit tests * repair the tests * fixed commented out unit tests * fix/add comments, stop calling the otel skd "old" * fix go.mod Co-authored-by: Joshua MacDonald --- .gitignore | 1 + go.mod | 32 +- go.sum | 65 ++- .../instrumentation/cputime/cputime_test.go | 122 +++-- lightstep/instrumentation/go.mod | 14 +- lightstep/instrumentation/go.sum | 30 +- lightstep/instrumentation/host/host_test.go | 133 +++--- .../instrumentation/runtime/builtin_test.go | 84 ++-- lightstep/sdk/metric/example/example_test.go | 12 +- lightstep/sdk/metric/example/go.mod | 21 +- lightstep/sdk/metric/example/go.sum | 47 +- .../sdk/metric/exporters/otlp/clients.go | 43 -- .../sdk/metric/exporters/otlp/exporter.go | 129 ----- .../metric/exporters/otlp/internal/config.go | 1 + .../otlp/internal/envconfig/envconfig.go | 82 +++- .../otlp/internal/envconfig/envconfig_test.go | 142 +++++- .../metric/exporters/otlp/internal/header.go | 25 + .../{options.go => internal/header_test.go} | 14 +- .../otlp/internal/otlptest/otlptest.go | 7 - .../exporters/otlp/internal/partialsuccess.go | 64 +++ .../otlp/internal/partialsuccess_test.go | 43 ++ .../exporters/otlp/otlpmetric/client.go | 58 +++ .../envconfig_test.go => otlpmetric/doc.go} | 7 +- .../exporters/otlp/otlpmetric/exporter.go | 147 ++++++ .../internal/oconf}/envconfig.go | 32 +- .../internal/oconf}/options.go | 45 +- .../internal/oconf}/options_test.go | 180 ++++--- .../internal/oconf}/optiontypes.go | 2 +- .../internal/oconf}/tls.go | 2 +- .../otlp/otlpmetric/internal/otest/client.go | 302 ++++++++++++ .../otlpmetric/internal/otest/client_test.go | 77 +++ .../otlpmetric/internal/otest/collector.go | 444 ++++++++++++++++++ .../internal/transform}/attribute.go | 2 +- .../internal/transform}/attribute_test.go | 2 +- .../internal/transform}/metric.go | 2 +- .../internal/transform}/metric_test.go | 2 +- .../internal/transform}/resource.go | 2 +- .../internal/transform}/resource_test.go | 2 +- .../otlp/otlpmetric/otlpmetricgrpc/client.go | 225 +++++++++ .../otlpmetric/otlpmetricgrpc/client_test.go | 142 ++++++ .../otlp/otlpmetric/otlpmetricgrpc/config.go | 257 ++++++++++ .../otlp/otlpmetric/otlpmetricgrpc/doc.go | 20 + .../otlpmetric/otlpmetricgrpc/example_test.go | 42 ++ lightstep/sdk/metric/go.mod | 34 +- lightstep/sdk/metric/go.sum | 61 +-- .../internal/global/internal_logging.go | 63 +++ .../sdk/metric/internal/syncstate/sync.go | 3 + pipelines/common.go | 15 +- pipelines/go.mod | 40 +- pipelines/go.sum | 69 +-- pipelines/metrics.go | 94 ++-- 51 files changed, 2798 insertions(+), 686 deletions(-) delete mode 100644 lightstep/sdk/metric/exporters/otlp/clients.go delete mode 100644 lightstep/sdk/metric/exporters/otlp/exporter.go create mode 100644 lightstep/sdk/metric/exporters/otlp/internal/header.go rename lightstep/sdk/metric/exporters/otlp/{options.go => internal/header_test.go} (62%) create mode 100644 lightstep/sdk/metric/exporters/otlp/internal/partialsuccess.go create mode 100644 lightstep/sdk/metric/exporters/otlp/internal/partialsuccess_test.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/client.go rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig/envconfig_test.go => otlpmetric/doc.go} (57%) create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/exporter.go rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig => otlpmetric/internal/oconf}/envconfig.go (75%) rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig => otlpmetric/internal/oconf}/options.go (85%) rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig => otlpmetric/internal/oconf}/options_test.go (65%) rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig => otlpmetric/internal/oconf}/optiontypes.go (94%) rename lightstep/sdk/metric/exporters/otlp/{internal/otlpconfig => otlpmetric/internal/oconf}/tls.go (91%) create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client_test.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/collector.go rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/attribute.go (96%) rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/attribute_test.go (99%) rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/metric.go (98%) rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/metric_test.go (99%) rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/resource.go (93%) rename lightstep/sdk/metric/exporters/otlp/{internal/metrictransform => otlpmetric/internal/transform}/resource_test.go (98%) create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go create mode 100644 lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go create mode 100644 lightstep/sdk/metric/internal/global/internal_logging.go diff --git a/.gitignore b/.gitignore index 1d1dcb06..24254df7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ Thumbs.db coverage.* go.work go.work.sum +vendor/ diff --git a/go.mod b/go.mod index 1615d8e0..3605ed0a 100644 --- a/go.mod +++ b/go.mod @@ -7,14 +7,14 @@ require ( github.com/lightstep/otel-launcher-go/pipelines v1.11.1 github.com/sethvargo/go-envconfig v0.8.2 github.com/stretchr/testify v1.8.1 - go.opentelemetry.io/otel v1.11.1 - go.opentelemetry.io/otel/metric v0.33.0 - go.opentelemetry.io/otel/sdk v1.11.1 - go.opentelemetry.io/otel/trace v1.11.1 + go.opentelemetry.io/otel v1.11.2 + go.opentelemetry.io/otel/metric v0.34.0 + go.opentelemetry.io/otel/sdk v1.11.2 + go.opentelemetry.io/otel/trace v1.11.2 ) require ( - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -34,22 +34,22 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opentelemetry.io/contrib/instrumentation/host v0.36.4 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.11.1 // indirect - go.opentelemetry.io/contrib/propagators/ot v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 // indirect - go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.12.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.12.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 // indirect + go.opentelemetry.io/otel/sdk/metric v0.34.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.4.0 // indirect google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 // indirect - google.golang.org/grpc v1.50.1 // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 89cc3953..f6515044 100644 --- a/go.sum +++ b/go.sum @@ -35,9 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -192,30 +191,30 @@ go.opentelemetry.io/contrib/instrumentation/host v0.36.4 h1:2D0q/69KewnkCkOI9I9u go.opentelemetry.io/contrib/instrumentation/host v0.36.4/go.mod h1:IQdse+GFHec/g2M4wtj6cE4uA5PJGQjjXP/602LjHBQ= go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4 h1:7AY5NdRzyU5s1ek3E4VK3FBnPtQ6La1i7sIn9hNgjsk= go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4/go.mod h1:yFSLOnffweT7Es+IzY1DF5KP0xa2Wl15SJfKqAyDXq8= -go.opentelemetry.io/contrib/propagators/b3 v1.11.1 h1:icQ6ttRV+r/2fnU46BIo/g/mPu6Rs5Ug8Rtohe3KqzI= -go.opentelemetry.io/contrib/propagators/b3 v1.11.1/go.mod h1:ECIveyMXgnl4gorxFcA7RYjJY/Ql9n20ubhbfDc3QfA= -go.opentelemetry.io/contrib/propagators/ot v1.11.1 h1:iezQwYW2sAaXwbXXA6Zg+PLjNnzc+M4hLKvOR6Q/CvI= -go.opentelemetry.io/contrib/propagators/ot v1.11.1/go.mod h1:oBced35DewKV7xvvIWC/oCaCFvthvTa6zjyvP2JhPAY= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 h1:H0+xwv4shKw0gfj/ZqR13qO2N/dBQogB1OcRjJjV39Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0/go.mod h1:nkenGD8vcvs0uN6WhR90ZVHQlgDsRmXicnNadMnk+XQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 h1:BaQ2xM5cPmldVCMvbLoy5tcLUhXCtIhItDYBNw83B7Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0/go.mod h1:VRr8tlXQEsTdesDCh0qBe2iKDWhpi3ZqDYw6VlZ8MhI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 h1:MEQNafcNCB0uQIti/oHgU7CZpUMYQ7qigBwMVKycHvc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1/go.mod h1:19O5I2U5iys38SsmT2uDJja/300woyzE1KPIQxEUBUc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 h1:LYyG/f1W/jzAix16jbksJfMQFpOH/Ma6T639pVPMgfI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1/go.mod h1:QrRRQiY3kzAoYPNLP0W/Ikg0gR6V3LMc+ODSxr7yyvg= -go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= -go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07 h1:nOHjEUsL5x134vpNDPwCZdp+EmvVE6qcPp9g7gWbf2E= -go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07/go.mod h1:Wx4hLV+yy0fBWE4doK+3sK8O6gcaVHRf0BRvwOzl+jI= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/contrib/propagators/b3 v1.12.0 h1:OtfTF8bneN8qTeo/j92kcvc0iDDm4bm/c3RzaUJfiu0= +go.opentelemetry.io/contrib/propagators/b3 v1.12.0/go.mod h1:0JDB4elfPUWGsCH/qhaMkDzP1l8nB0ANVx8zXuAYEwg= +go.opentelemetry.io/contrib/propagators/ot v1.12.0 h1:6yNXAAQW08uIwgQKQvGC/0/5KkUCZmOcwSjj8zYg1ZU= +go.opentelemetry.io/contrib/propagators/ot v1.12.0/go.mod h1:t2n1RTxGm14/AEMSELd0jJo3NBjeEHnDtCRYXKTl0Ak= +go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= +go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 h1:htgM8vZIF8oPSCxa341e3IZ4yr/sKxgu8KZYllByiVY= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2/go.mod h1:rqbht/LlhVBgn5+k3M5QK96K5Xb0DvXpMJ5SFQpY6uw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 h1:kpskzLZ60cJ48SJ4uxWa6waBL+4kSV6nVK8rP+QM8Wg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0/go.mod h1:4+x3i62TEegDHuzNva0bMcAN8oUi5w4liGb1d/VgPYo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 h1:e7kFb4pJLbhJgAwUdoVTHzB9pGujs5O8/7gFyZL88fg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0/go.mod h1:3x00m9exjIbhK+zTO4MsCSlfbVmgvLP0wjDgDKa/8bw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 h1:fqR1kli93643au1RKo0Uma3d2aPQKT+WBKfTSBaKbOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2/go.mod h1:5Qn6qvgkMsLDX+sYK64rHb1FPhpn0UtxF+ouX1uhyJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 h1:ERwKPn9Aer7Gxsc0+ZlutlH1bEEAUXAUhqm3Y45ABbk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2/go.mod h1:jWZUM2MWhWCJ9J9xVbRx7tzK1mXKpAlze4CeulycwVY= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= +go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= +go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= +go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= +go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= +go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= +go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -286,8 +285,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -344,8 +343,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -464,8 +463,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp 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/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 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= diff --git a/lightstep/instrumentation/cputime/cputime_test.go b/lightstep/instrumentation/cputime/cputime_test.go index 7672091a..5bcfcc54 100644 --- a/lightstep/instrumentation/cputime/cputime_test.go +++ b/lightstep/instrumentation/cputime/cputime_test.go @@ -4,14 +4,13 @@ // 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 +// 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 cputime import ( @@ -24,50 +23,68 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/sdk/metric/export/aggregation" - "go.opentelemetry.io/otel/sdk/metric/metrictest" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" ) -func getMetric(exp *metrictest.Exporter, name string, lbl attribute.KeyValue) float64 { - for _, r := range exp.GetRecords() { - if r.InstrumentName != name { +func getMetric(metrics []metricdata.Metrics, name string, lbl attribute.KeyValue) float64 { + for _, m := range metrics { + fmt.Println(m.Name) + if m.Name != name { continue } - if lbl.Key != "" { - foundAttribute := false - for _, haveLabel := range r.Attributes { - if haveLabel != lbl { - continue + switch dt := m.Data.(type) { + case metricdata.Gauge[int64]: + if !lbl.Valid() { + return float64(dt.DataPoints[0].Value) + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return float64(p.Value) } - foundAttribute = true - break } - if !foundAttribute { - continue + case metricdata.Gauge[float64]: + if !lbl.Valid() { + return dt.DataPoints[0].Value + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return p.Value + } + } + case metricdata.Sum[int64]: + if !lbl.Valid() { + return float64(dt.DataPoints[0].Value) + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return float64(p.Value) + } + } + case metricdata.Sum[float64]: + if !lbl.Valid() { + return dt.DataPoints[0].Value + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return p.Value + } } - } - - switch r.AggregationKind { - case aggregation.SumKind, aggregation.HistogramKind: - return r.Sum.CoerceToFloat64(r.NumberKind) - case aggregation.LastValueKind: - return r.LastValue.CoerceToFloat64(r.NumberKind) default: - panic(fmt.Sprintf("invalid aggregation type: %v", r.AggregationKind)) + panic(fmt.Sprintf("invalid aggregation type: %v", dt)) } } - panic("Could not locate a metric in test output") + panic(fmt.Sprintf("Could not locate a metric in test output, name: %s, keyValue: %v", name, lbl)) } func TestProcessCPU(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() - err := Start( - WithMeterProvider(provider), - ) - require.NoError(t, err) - ctx := context.Background() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) + + err := Start(WithMeterProvider(provider)) + require.NoError(t, err) // This is a second copy of the same source of information. // We ultimately have to trust the information source, the @@ -88,10 +105,12 @@ func TestProcessCPU(t *testing.T) { beforeUser, beforeSystem, _, _ := c.getProcessTimes(ctx) - require.NoError(t, exp.Collect(ctx)) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) - processUser := getMetric(exp, "process.cpu.time", AttributeCPUTimeUser[0]) - processSystem := getMetric(exp, "process.cpu.time", AttributeCPUTimeSystem[0]) + processUser := getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeUser[0]) + processSystem := getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeSystem[0]) afterUser, afterSystem, _, _ := c.getProcessTimes(ctx) @@ -116,13 +135,18 @@ func TestProcessUptime(t *testing.T) { processStartTime = save }() - provider, exp := metrictest.NewTestMeterProvider() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) + c, err := newCputime(config{MeterProvider: provider}) require.NoError(t, err) require.NoError(t, c.register()) - require.NoError(t, exp.Collect(ctx)) - procUptime := getMetric(exp, "process.uptime", attribute.KeyValue{}) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) + + procUptime := getMetric(data.ScopeMetrics[0].Metrics, "process.uptime", attribute.KeyValue{}) require.LessOrEqual(t, expectUptime, procUptime) } @@ -130,17 +154,21 @@ func TestProcessUptime(t *testing.T) { func TestProcessGCCPUTime(t *testing.T) { ctx := context.Background() - provider, exp := metrictest.NewTestMeterProvider() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) c, err := newCputime(config{ MeterProvider: provider, }) require.NoError(t, err) require.NoError(t, c.register()) - require.NoError(t, exp.Collect(ctx)) - initialUtime := getMetric(exp, "process.cpu.time", AttributeCPUTimeUser[0]) - initialStime := getMetric(exp, "process.cpu.time", AttributeCPUTimeSystem[0]) - initialGCtime := getMetric(exp, "process.runtime.go.gc.cpu.time", attribute.KeyValue{}) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) + + initialUtime := getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeUser[0]) + initialStime := getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeSystem[0]) + initialGCtime := getMetric(data.ScopeMetrics[0].Metrics, "process.runtime.go.gc.cpu.time", attribute.KeyValue{}) // Make garbage for i := 0; i < 2; i++ { @@ -151,10 +179,12 @@ func TestProcessGCCPUTime(t *testing.T) { require.Less(t, 0, len(garbage)) runtime.GC() - require.NoError(t, exp.Collect(ctx)) - utime := -initialUtime + getMetric(exp, "process.cpu.time", AttributeCPUTimeUser[0]) - stime := -initialStime + getMetric(exp, "process.cpu.time", AttributeCPUTimeSystem[0]) - gctime := -initialGCtime + getMetric(exp, "process.runtime.go.gc.cpu.time", attribute.KeyValue{}) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) + utime := -initialUtime + getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeUser[0]) + stime := -initialStime + getMetric(data.ScopeMetrics[0].Metrics, "process.cpu.time", AttributeCPUTimeSystem[0]) + gctime := -initialGCtime + getMetric(data.ScopeMetrics[0].Metrics, "process.runtime.go.gc.cpu.time", attribute.KeyValue{}) require.LessOrEqual(t, gctime, utime+stime) } diff --git a/lightstep/instrumentation/go.mod b/lightstep/instrumentation/go.mod index 00a11df9..b081809d 100644 --- a/lightstep/instrumentation/go.mod +++ b/lightstep/instrumentation/go.mod @@ -4,10 +4,11 @@ go 1.18 require ( github.com/shirou/gopsutil/v3 v3.22.8 - github.com/stretchr/testify v1.8.0 - go.opentelemetry.io/otel v1.10.0 - go.opentelemetry.io/otel/metric v0.31.0 - go.opentelemetry.io/otel/sdk/metric v0.31.0 + github.com/stretchr/testify v1.8.1 + go.opentelemetry.io/otel v1.11.2 + go.opentelemetry.io/otel/metric v0.34.0 + go.opentelemetry.io/otel/sdk v1.11.2 + go.opentelemetry.io/otel/sdk/metric v0.34.0 ) require ( @@ -21,8 +22,7 @@ require ( github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel/sdk v1.10.0 // indirect - go.opentelemetry.io/otel/trace v1.10.0 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + go.opentelemetry.io/otel/trace v1.11.2 // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/lightstep/instrumentation/go.sum b/lightstep/instrumentation/go.sum index 484e7528..4e2c0d86 100644 --- a/lightstep/instrumentation/go.sum +++ b/lightstep/instrumentation/go.sum @@ -1,4 +1,3 @@ -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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= @@ -10,8 +9,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -22,30 +21,33 @@ github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHS github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= -go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= -go.opentelemetry.io/otel/metric v0.31.0 h1:6SiklT+gfWAwWUR0meEMxQBtihpiEs4c+vL9spDTqUs= -go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= -go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= -go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= -go.opentelemetry.io/otel/sdk/metric v0.31.0 h1:2sZx4R43ZMhJdteKAlKoHvRgrMp53V1aRxvEf5lCq8Q= -go.opentelemetry.io/otel/sdk/metric v0.31.0/go.mod h1:fl0SmNnX9mN9xgU6OLYLMBMrNAsaZQi7qBwprwO3abk= -go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E= -go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= +go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= +go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= +go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= +go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= +go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= +go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= +go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= +go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/lightstep/instrumentation/host/host_test.go b/lightstep/instrumentation/host/host_test.go index f6e36f04..0ab29ed6 100644 --- a/lightstep/instrumentation/host/host_test.go +++ b/lightstep/instrumentation/host/host_test.go @@ -4,14 +4,13 @@ // 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 +// 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 host import ( @@ -30,48 +29,68 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/sdk/metric/export/aggregation" - "go.opentelemetry.io/otel/sdk/metric/metrictest" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" ) -func getMetric(exp *metrictest.Exporter, name string, lbl attribute.KeyValue) float64 { - for _, r := range exp.GetRecords() { - if r.InstrumentName != name { +func getMetric(metrics []metricdata.Metrics, name string, lbl attribute.KeyValue) float64 { + for _, m := range metrics { + fmt.Println(m.Name) + if m.Name != name { continue } - if lbl.Key != "" { - foundAttribute := false - for _, haveLabel := range r.Attributes { - if haveLabel != lbl { - continue + switch dt := m.Data.(type) { + case metricdata.Gauge[int64]: + if !lbl.Valid() { + return float64(dt.DataPoints[0].Value) + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return float64(p.Value) } - foundAttribute = true - break } - if !foundAttribute { - continue + case metricdata.Gauge[float64]: + if !lbl.Valid() { + return dt.DataPoints[0].Value + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return p.Value + } + } + case metricdata.Sum[int64]: + if !lbl.Valid() { + return float64(dt.DataPoints[0].Value) + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return float64(p.Value) + } + } + case metricdata.Sum[float64]: + if !lbl.Valid() { + return dt.DataPoints[0].Value + } + for _, p := range dt.DataPoints { + if val, ok := p.Attributes.Value(lbl.Key); ok && val.Emit() == lbl.Value.Emit() { + return p.Value + } } - } - - switch r.AggregationKind { - case aggregation.SumKind, aggregation.HistogramKind: - return r.Sum.CoerceToFloat64(r.NumberKind) - case aggregation.LastValueKind: - return r.LastValue.CoerceToFloat64(r.NumberKind) default: - panic(fmt.Sprintf("invalid aggregation type: %v", r.AggregationKind)) + panic(fmt.Sprintf("invalid aggregation type: %v", dt)) } } - panic("Could not locate a metric in test output") + panic(fmt.Sprintf("Could not locate a metric in test output, name: %s, keyValue: %v", name, lbl)) } func TestHostCPU(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() - err := Start( - WithMeterProvider(provider), - ) - assert.NoError(t, err) + ctx := context.Background() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) + + err := Start(WithMeterProvider(provider)) + require.NoError(t, err) // Note: we use a different library // ("github.com/shirou/gopsutil/v3/process") to verify process @@ -79,8 +98,6 @@ func TestHostCPU(t *testing.T) { proc, err := process.NewProcess(int32(os.Getpid())) require.NoError(t, err) - ctx := context.Background() - hostBefore, err := cpu.TimesWithContext(ctx, false) require.NoError(t, err) @@ -93,10 +110,12 @@ func TestHostCPU(t *testing.T) { require.NoError(t, err) } - require.NoError(t, exp.Collect(ctx)) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) - hostUser := getMetric(exp, "system.cpu.time", AttributeCPUTimeUser[0]) - hostSystem := getMetric(exp, "system.cpu.time", AttributeCPUTimeSystem[0]) + hostUser := getMetric(data.ScopeMetrics[0].Metrics, "system.cpu.time", AttributeCPUTimeUser[0]) + hostSystem := getMetric(data.ScopeMetrics[0].Metrics, "system.cpu.time", AttributeCPUTimeSystem[0]) hostAfter, err := cpu.TimesWithContext(ctx, false) require.NoError(t, err) @@ -122,31 +141,33 @@ func TestHostCPU(t *testing.T) { } func TestHostMemory(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() - err := Start( - WithMeterProvider(provider), - ) - assert.NoError(t, err) - ctx := context.Background() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) + + err := Start(WithMeterProvider(provider)) + require.NoError(t, err) + vMem, err := mem.VirtualMemoryWithContext(ctx) require.NoError(t, err) - require.NoError(t, exp.Collect(ctx)) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) - hostUsed := getMetric(exp, "system.memory.usage", AttributeMemoryUsed[0]) + hostUsed := getMetric(data.ScopeMetrics[0].Metrics, "system.memory.usage", AttributeMemoryUsed[0]) assert.Greater(t, hostUsed, 0.0) assert.LessOrEqual(t, hostUsed, float64(vMem.Total)) - hostAvailable := getMetric(exp, "system.memory.usage", AttributeMemoryAvailable[0]) + hostAvailable := getMetric(data.ScopeMetrics[0].Metrics, "system.memory.usage", AttributeMemoryAvailable[0]) assert.GreaterOrEqual(t, hostAvailable, 0.0) assert.Less(t, hostAvailable, float64(vMem.Total)) - hostUsedUtil := getMetric(exp, "system.memory.utilization", AttributeMemoryUsed[0]) + hostUsedUtil := getMetric(data.ScopeMetrics[0].Metrics, "system.memory.utilization", AttributeMemoryUsed[0]) assert.Greater(t, hostUsedUtil, 0.0) assert.LessOrEqual(t, hostUsedUtil, 1.0) - hostAvailableUtil := getMetric(exp, "system.memory.utilization", AttributeMemoryAvailable[0]) + hostAvailableUtil := getMetric(data.ScopeMetrics[0].Metrics, "system.memory.utilization", AttributeMemoryAvailable[0]) assert.GreaterOrEqual(t, hostAvailableUtil, 0.0) assert.Less(t, hostAvailableUtil, 1.0) @@ -194,13 +215,13 @@ func sendBytes(t *testing.T, count int) error { } func TestHostNetwork(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() - err := Start( - WithMeterProvider(provider), - ) - assert.NoError(t, err) - ctx := context.Background() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) + + err := Start(WithMeterProvider(provider)) + require.NoError(t, err) + hostBefore, err := net.IOCountersWithContext(ctx, false) require.NoError(t, err) @@ -217,9 +238,11 @@ func TestHostNetwork(t *testing.T) { uint64(howMuch) <= hostAfter[0].BytesRecv-hostBefore[0].BytesRecv }, 30*time.Second, time.Second/2) - require.NoError(t, exp.Collect(ctx)) - hostTransmit := getMetric(exp, "system.network.io", AttributeNetworkTransmit[0]) - hostReceive := getMetric(exp, "system.network.io", AttributeNetworkReceive[0]) + data, err := reader.Collect(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(data.ScopeMetrics)) + hostTransmit := getMetric(data.ScopeMetrics[0].Metrics, "system.network.io", AttributeNetworkTransmit[0]) + hostReceive := getMetric(data.ScopeMetrics[0].Metrics, "system.network.io", AttributeNetworkReceive[0]) // Check that the recorded measurements reflect the same change: require.LessOrEqual(t, uint64(howMuch), uint64(hostTransmit)-hostBefore[0].BytesSent) diff --git a/lightstep/instrumentation/runtime/builtin_test.go b/lightstep/instrumentation/runtime/builtin_test.go index f2c8ab5a..7955d0b3 100644 --- a/lightstep/instrumentation/runtime/builtin_test.go +++ b/lightstep/instrumentation/runtime/builtin_test.go @@ -11,7 +11,6 @@ // 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 runtime import ( @@ -23,48 +22,64 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/sdk/metric/metrictest" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" ) // prefix is mandatory for this library, however the "go." part is not. const expectPrefix = "process.runtime.go." -var expectLib = metrictest.Scope{ - InstrumentationName: "otel-launcher-go/runtime", - InstrumentationVersion: "", - SchemaURL: "", +var expectScope = instrumentation.Scope{ + Name: "otel-launcher-go/runtime", } // TestBuiltinRuntimeMetrics tests the real output of the library to // ensure expected prefix, instrumentation scope, and empty // attributes. func TestBuiltinRuntimeMetrics(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) err := Start(WithMeterProvider(provider)) + require.NoError(t, err) + data, err := reader.Collect(context.Background()) require.NoError(t, err) - require.NoError(t, exp.Collect(context.Background())) + require.Equal(t, 1, len(data.ScopeMetrics)) + require.Equal(t, expectScope, data.ScopeMetrics[0].Scope) - // Counts are >1 for metrics that are totalized. expect := expectRuntimeMetrics allNames := map[string]int{} // Note: metrictest library lacks a way to distinguish // monotonic vs not or to test the unit. This will be fixed in // the new SDK, all the pieces untested here. - for _, rec := range exp.Records { - require.True(t, strings.HasPrefix(rec.InstrumentName, expectPrefix), "%s", rec.InstrumentName) - name := rec.InstrumentName[len(expectPrefix):] - - require.Equal(t, expectLib, rec.InstrumentationLibrary) + for _, inst := range data.ScopeMetrics[0].Metrics { + require.True(t, strings.HasPrefix(inst.Name, expectPrefix), "%s", inst.Name) + name := inst.Name[len(expectPrefix):] + var attrs attribute.Set + switch dt := inst.Data.(type) { + case metricdata.Gauge[int64]: + require.Equal(t, 1, len(dt.DataPoints)) + attrs = dt.DataPoints[0].Attributes + case metricdata.Gauge[float64]: + require.Equal(t, 1, len(dt.DataPoints)) + attrs = dt.DataPoints[0].Attributes + case metricdata.Sum[int64]: + require.Equal(t, 1, len(dt.DataPoints)) + attrs = dt.DataPoints[0].Attributes + case metricdata.Sum[float64]: + require.Equal(t, 1, len(dt.DataPoints)) + attrs = dt.DataPoints[0].Attributes + } if expect[name] > 1 { - require.Equal(t, 1, len(rec.Attributes)) + require.Equal(t, 1, attrs.Len()) } else { - require.Equal(t, 1, expect[name], "for %v", rec.InstrumentName) - require.Equal(t, []attribute.KeyValue(nil), rec.Attributes) + require.Equal(t, 1, expect[name], "for %v", inst.Name) + require.Equal(t, 0, attrs.Len()) } allNames[name]++ } @@ -172,11 +187,13 @@ func makeTestCase() (allFunc, readFunc, map[string]map[string]metrics.Value) { // TestMetricTranslation validates the translation logic using // synthetic metric names and values. func TestMetricTranslation(t *testing.T) { - provider, exp := metrictest.NewTestMeterProvider() + reader := metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(reader)) af, rf, mapping := makeTestCase() br := newBuiltinRuntime(provider.Meter("test"), af, rf) - br.register() + err := br.register() + require.NoError(t, err) expectRecords := 0 for _, values := range mapping { @@ -187,30 +204,39 @@ func TestMetricTranslation(t *testing.T) { } } - require.NoError(t, exp.Collect(context.Background())) + data, err := reader.Collect(context.Background()) + require.NoError(t, err) require.Equal(t, 10, expectRecords) - for _, rec := range exp.Records { + require.Equal(t, 1, len(data.ScopeMetrics)) + require.Equal(t, "test", data.ScopeMetrics[0].Scope.Name) + + for _, inst := range data.ScopeMetrics[0].Metrics { // Test the special cases are present always: - require.True(t, strings.HasPrefix(rec.InstrumentName, expectPrefix), "%s", rec.InstrumentName) - name := rec.InstrumentName[len(expectPrefix):] + require.True(t, strings.HasPrefix(inst.Name, expectPrefix), "%s", inst.Name) + name := inst.Name[len(expectPrefix):] + + require.Equal(t, 1, len(inst.Data.(metricdata.Sum[int64]).DataPoints)) + + sum := inst.Data.(metricdata.Sum[int64]).DataPoints[0].Value + attrs := inst.Data.(metricdata.Sum[int64]).DataPoints[0].Attributes // Note: only int64 is tested, we have no way to // generate Float64 values and Float64Hist values are // not implemented for testing. m := mapping[name] if len(m) == 1 { - require.Equal(t, mapping[name][""].Uint64(), uint64(rec.Sum.AsInt64())) + require.Equal(t, mapping[name][""].Uint64(), uint64(sum)) // no attributes - require.Equal(t, []attribute.KeyValue(nil), rec.Attributes) + require.Equal(t, 0, attrs.Len()) } else { require.Equal(t, 5, len(m)) - require.Equal(t, 1, len(rec.Attributes)) - require.Equal(t, rec.Attributes[0].Key, "class") - feature := rec.Attributes[0].Value.AsString() - require.Equal(t, mapping[name][feature].Uint64(), uint64(rec.Sum.AsInt64())) + require.Equal(t, 1, attrs.Len()) + require.Equal(t, attrs.ToSlice()[0].Key, "class") + feature := attrs.ToSlice()[0].Value.AsString() + require.Equal(t, mapping[name][feature].Uint64(), uint64(sum)) } } } diff --git a/lightstep/sdk/metric/example/example_test.go b/lightstep/sdk/metric/example/example_test.go index 7f4a9f23..0f5ee0e1 100644 --- a/lightstep/sdk/metric/example/example_test.go +++ b/lightstep/sdk/metric/example/example_test.go @@ -21,11 +21,11 @@ import ( // Note the SDK, exporter, and test appratus are from this repository lightstep "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric" - exporter "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp" + exporter "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + otlpmetricgrpc "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" otlptest "github.com/lightstep/otel-launcher-go/pipelines/test" // Note the gRPC/OTLP exporter and protocol are from the community SDK. - otlpmetricgrpc "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" otlpproto "go.opentelemetry.io/proto/otlp/metrics/v1" ) @@ -35,7 +35,8 @@ func ExampleMinimumConfig() { server := otlptest.NewServer() // Configure an exporter. - exp, _ := exporter.New(ctx, otlpmetricgrpc.NewClient( + client, _ := otlpmetricgrpc.NewClient( + ctx, // In a real scenario, replace the following three lines with, for example: // WithEndpoint("ingest.lightstep.com:443"). @@ -43,7 +44,8 @@ func ExampleMinimumConfig() { otlpmetricgrpc.WithInsecure(), otlpmetricgrpc.WithEndpoint(fmt.Sprint(otlptest.ServerName, ":", server.InsecureMetricsPort)), otlpmetricgrpc.WithHeaders(map[string]string{"lightstep-access-token": "${TOKEN}"}), - )) + ) + exp := exporter.New(client) // Configure the SDK. sdk := lightstep.NewMeterProvider( @@ -56,7 +58,7 @@ func ExampleMinimumConfig() { // Count once and shutdown. counter.Add(ctx, 1) - sdk.Shutdown(ctx) + _ = sdk.Shutdown(ctx) oneScope := server.MetricsRequests()[0].ResourceMetrics[0].ScopeMetrics[0] oneHeader := server.MetricsMDs()[0] diff --git a/lightstep/sdk/metric/example/go.mod b/lightstep/sdk/metric/example/go.mod index d743ca96..ef27259b 100644 --- a/lightstep/sdk/metric/example/go.mod +++ b/lightstep/sdk/metric/example/go.mod @@ -5,32 +5,29 @@ go 1.18 require ( github.com/lightstep/otel-launcher-go/lightstep/sdk/metric v1.11.1 github.com/lightstep/otel-launcher-go/pipelines v1.8.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 go.opentelemetry.io/proto/otlp v0.19.0 ) require ( - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/lightstep/go-expohisto v1.0.0 // indirect - go.opentelemetry.io/otel v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 // indirect - go.opentelemetry.io/otel/metric v0.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.11.1 // indirect - go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07 // indirect - go.opentelemetry.io/otel/trace v1.11.1 // indirect + go.opentelemetry.io/otel v1.11.2 // indirect + go.opentelemetry.io/otel/metric v0.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.11.2 // indirect + go.opentelemetry.io/otel/sdk/metric v0.34.0 // indirect + go.opentelemetry.io/otel/trace v1.11.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.4.0 // indirect google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 // indirect - google.golang.org/grpc v1.50.1 // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/lightstep/sdk/metric/example/go.sum b/lightstep/sdk/metric/example/go.sum index 5aa35fd2..36512852 100644 --- a/lightstep/sdk/metric/example/go.sum +++ b/lightstep/sdk/metric/example/go.sum @@ -35,8 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -153,7 +153,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -162,22 +162,19 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 h1:H0+xwv4shKw0gfj/ZqR13qO2N/dBQogB1OcRjJjV39Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0/go.mod h1:nkenGD8vcvs0uN6WhR90ZVHQlgDsRmXicnNadMnk+XQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 h1:BaQ2xM5cPmldVCMvbLoy5tcLUhXCtIhItDYBNw83B7Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0/go.mod h1:VRr8tlXQEsTdesDCh0qBe2iKDWhpi3ZqDYw6VlZ8MhI= -go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= -go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07 h1:nOHjEUsL5x134vpNDPwCZdp+EmvVE6qcPp9g7gWbf2E= -go.opentelemetry.io/otel/sdk/metric v0.31.1-0.20220826135333-55b49c407e07/go.mod h1:Wx4hLV+yy0fBWE4doK+3sK8O6gcaVHRf0BRvwOzl+jI= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= +go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 h1:htgM8vZIF8oPSCxa341e3IZ4yr/sKxgu8KZYllByiVY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 h1:kpskzLZ60cJ48SJ4uxWa6waBL+4kSV6nVK8rP+QM8Wg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 h1:e7kFb4pJLbhJgAwUdoVTHzB9pGujs5O8/7gFyZL88fg= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= +go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= +go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= +go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= +go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= +go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= +go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -247,8 +244,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -301,8 +298,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -421,8 +418,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp 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/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 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= diff --git a/lightstep/sdk/metric/exporters/otlp/clients.go b/lightstep/sdk/metric/exporters/otlp/clients.go deleted file mode 100644 index 47eefe84..00000000 --- a/lightstep/sdk/metric/exporters/otlp/clients.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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 otlp // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp" - -import ( - "context" - - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" -) - -// Client manages connections to the collector, handles the -// transformation of data into wire format, and the transmission of that -// data to the collector. -type Client interface { - // Start should establish connection(s) to endpoint(s). It is - // called just once by the exporter, so the implementation - // does not need to worry about idempotence and locking. - Start(ctx context.Context) error - // Stop should close the connections. The function is called - // only once by the exporter, so the implementation does not - // need to worry about idempotence, but it may be called - // concurrently with UploadMetrics, so proper - // locking is required. The function serves as a - // synchronization point - after the function returns, the - // process of closing connections is assumed to be finished. - Stop(ctx context.Context) error - // UploadMetrics should transform the passed metrics to the - // wire format and send it to the collector. May be called - // concurrently. - UploadMetrics(ctx context.Context, protoMetrics *metricpb.ResourceMetrics) error -} diff --git a/lightstep/sdk/metric/exporters/otlp/exporter.go b/lightstep/sdk/metric/exporters/otlp/exporter.go deleted file mode 100644 index 3572b105..00000000 --- a/lightstep/sdk/metric/exporters/otlp/exporter.go +++ /dev/null @@ -1,129 +0,0 @@ -// 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 otlp // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp" - -import ( - "context" - "errors" - "fmt" - "sync" - - "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric" - "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/data" - "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/metrictransform" -) - -var ( - errAlreadyStarted = errors.New("already started") -) - -// Exporter exports metrics data in the OTLP wire format. -type Exporter struct { - client Client - - mu sync.RWMutex - started bool - - startOnce sync.Once - stopOnce sync.Once -} - -// ExportMetrics exports a batch of metrics. -func (e *Exporter) ExportMetrics(ctx context.Context, metrics data.Metrics) error { - rm, err := metrictransform.Metrics(metrics) - if err != nil { - return err - } - if rm == nil { - return nil - } - - return e.client.UploadMetrics(ctx, rm) -} - -// Start establishes a connection to the receiving endpoint. -func (e *Exporter) Start(ctx context.Context) error { - var err = errAlreadyStarted - e.startOnce.Do(func() { - e.mu.Lock() - e.started = true - e.mu.Unlock() - err = e.client.Start(ctx) - }) - - return err -} - -func (e *Exporter) String() string { - return "otlp-lightstep" -} - -// ForceFlushMetrics calls ExportMetrics for an immediate export. -func (e *Exporter) ForceFlushMetrics(ctx context.Context, metrics data.Metrics) error { - return e.ExportMetrics(ctx, metrics) -} - -// ShutdownMetrics flushes all exports and closes all connections to the receiving endpoint. -func (e *Exporter) ShutdownMetrics(ctx context.Context, metrics data.Metrics) error { - e.mu.RLock() - started := e.started - e.mu.RUnlock() - - if !started { - return nil - } - - var err error - - e.stopOnce.Do(func() { - err = e.ExportMetrics(ctx, metrics) - if stopErr := e.client.Stop(ctx); stopErr != nil && err == nil { - err = stopErr - } else if stopErr != nil { - err = fmt.Errorf("shutdown flush: %w, stop: %v", err, stopErr) - } - e.mu.Lock() - e.started = false - e.mu.Unlock() - }) - - return err -} - -var _ metric.PushExporter = (*Exporter)(nil) - -// New constructs a new Exporter and starts it. -func New(ctx context.Context, client Client, opts ...Option) (*Exporter, error) { - exp := NewUnstarted(client, opts...) - if err := exp.Start(ctx); err != nil { - return nil, err - } - return exp, nil -} - -// NewUnstarted constructs a new Exporter and does not start it. -func NewUnstarted(client Client, opts ...Option) *Exporter { - cfg := config{} - - for _, opt := range opts { - cfg = opt.apply(cfg) - } - - e := &Exporter{ - client: client, - } - - return e -} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/config.go b/lightstep/sdk/metric/exporters/otlp/internal/config.go index 73de4822..2752ddaa 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/config.go +++ b/lightstep/sdk/metric/exporters/otlp/internal/config.go @@ -13,6 +13,7 @@ // limitations under the License. // Package internal contains common functionality for all OTLP exporters. +// Other than config.go, these files were forked from "go.opentelemetry.io/otel/exporters/otlp/internal". package internal // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" import ( diff --git a/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig.go b/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig.go index d23e49a1..2bf48e3a 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig.go +++ b/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package envconfig was forked from "go.opentelemetry.io/otel/exporters/otlp/internal/envconfig". package envconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/envconfig" import ( @@ -23,6 +24,8 @@ import ( "strconv" "strings" "time" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/internal/global" ) // ConfigFn is the generic function used to set a config. @@ -59,13 +62,26 @@ func WithString(n string, fn func(string)) func(e *EnvOptionsReader) { } } +// WithBool returns a ConfigFn that reads the environment variable n and if it exists passes its parsed bool value to fn. +func WithBool(n string, fn func(bool)) ConfigFn { + return func(e *EnvOptionsReader) { + if v, ok := e.GetEnvValue(n); ok { + b := strings.ToLower(v) == "true" + fn(b) + } + } +} + // WithDuration retrieves the specified config and passes it to ConfigFn as a duration. func WithDuration(n string, fn func(time.Duration)) func(e *EnvOptionsReader) { return func(e *EnvOptionsReader) { if v, ok := e.GetEnvValue(n); ok { - if d, err := strconv.Atoi(v); err == nil { - fn(time.Duration(d) * time.Millisecond) + d, err := strconv.Atoi(v) + if err != nil { + global.Error(err, "parse duration", "input", v) + return } + fn(time.Duration(d) * time.Millisecond) } } } @@ -83,23 +99,59 @@ func WithHeaders(n string, fn func(map[string]string)) func(e *EnvOptionsReader) func WithURL(n string, fn func(*url.URL)) func(e *EnvOptionsReader) { return func(e *EnvOptionsReader) { if v, ok := e.GetEnvValue(n); ok { - if u, err := url.Parse(v); err == nil { - fn(u) + u, err := url.Parse(v) + if err != nil { + global.Error(err, "parse url", "input", v) + return } + fn(u) } } } -// WithTLSConfig retrieves the specified config and passes it to ConfigFn as a crypto/tls.Config. -func WithTLSConfig(n string, fn func(*tls.Config)) func(e *EnvOptionsReader) { +// WithCertPool returns a ConfigFn that reads the environment variable n as a filepath to a TLS certificate pool. If it exists, it is parsed as a crypto/x509.CertPool and it is passed to fn. +func WithCertPool(n string, fn func(*x509.CertPool)) ConfigFn { return func(e *EnvOptionsReader) { if v, ok := e.GetEnvValue(n); ok { - if b, err := e.ReadFile(v); err == nil { - if c, err := createTLSConfig(b); err == nil { - fn(c) - } + b, err := e.ReadFile(v) + if err != nil { + global.Error(err, "read tls ca cert file", "file", v) + return + } + c, err := createCertPool(b) + if err != nil { + global.Error(err, "create tls cert pool") + return } + fn(c) + } + } +} + +// WithClientCert returns a ConfigFn that reads the environment variable nc and nk as filepaths to a client certificate and key pair. If they exists, they are parsed as a crypto/tls.Certificate and it is passed to fn. +func WithClientCert(nc, nk string, fn func(tls.Certificate)) ConfigFn { + return func(e *EnvOptionsReader) { + vc, okc := e.GetEnvValue(nc) + vk, okk := e.GetEnvValue(nk) + if !okc || !okk { + return + } + cert, err := e.ReadFile(vc) + if err != nil { + global.Error(err, "read tls client cert", "file", vc) + return } + key, err := e.ReadFile(vk) + if err != nil { + global.Error(err, "read tls client key", "file", vk) + return + } + crt, err := tls.X509KeyPair(cert, key) + if err != nil { + global.Error(err, "create tls client key pair") + return + } + fn(crt) } } @@ -117,15 +169,18 @@ func stringToHeader(value string) map[string]string { for _, header := range headersPairs { nameValue := strings.SplitN(header, "=", 2) if len(nameValue) < 2 { + global.Error(errors.New("missing '="), "parse headers", "input", nameValue) continue } name, err := url.QueryUnescape(nameValue[0]) if err != nil { + global.Error(err, "escape header key", "key", nameValue[0]) continue } trimmedName := strings.TrimSpace(name) value, err := url.QueryUnescape(nameValue[1]) if err != nil { + global.Error(err, "escape header value", "value", nameValue[1]) continue } trimmedValue := strings.TrimSpace(value) @@ -136,13 +191,10 @@ func stringToHeader(value string) map[string]string { return headers } -func createTLSConfig(certBytes []byte) (*tls.Config, error) { +func createCertPool(certBytes []byte) (*x509.CertPool, error) { cp := x509.NewCertPool() if ok := cp.AppendCertsFromPEM(certBytes); !ok { return nil, errors.New("failed to append certificate to the cert pool") } - - return &tls.Config{ - RootCAs: cp, - }, nil + return cp, nil } diff --git a/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig_test.go b/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig_test.go index 8ea265fb..75cdef2a 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig_test.go +++ b/lightstep/sdk/metric/exporters/otlp/internal/envconfig/envconfig_test.go @@ -16,6 +16,8 @@ package envconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk import ( "crypto/tls" + "crypto/x509" + "errors" "net/url" "testing" "time" @@ -23,22 +25,31 @@ import ( "github.com/stretchr/testify/assert" ) +const WeakKey = ` +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEbrSPmnlSOXvVzxCyv+VR3a0HDeUTvOcqrdssZ2k4gFoAoGCCqGSM49 +AwEHoUQDQgAEDMTfv75J315C3K9faptS9iythKOMEeV/Eep73nWX531YAkmmwBSB +2dXRD/brsgLnfG57WEpxZuY7dPRbxu33BA== +-----END EC PRIVATE KEY----- +` + const WeakCertificate = ` -----BEGIN CERTIFICATE----- -MIIBhzCCASygAwIBAgIRANHpHgAWeTnLZpTSxCKs0ggwCgYIKoZIzj0EAwIwEjEQ -MA4GA1UEChMHb3RlbC1nbzAeFw0yMTA0MDExMzU5MDNaFw0yMTA0MDExNDU5MDNa -MBIxEDAOBgNVBAoTB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS9 -nWSkmPCxShxnp43F+PrOtbGV7sNfkbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0Z -sJCLHGogQsYnWJBXUZOVo2MwYTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI -KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAsBgNVHREEJTAjgglsb2NhbGhvc3SHEAAA -AAAAAAAAAAAAAAAAAAGHBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhANwZVVKvfvQ/ -1HXsTvgH+xTQswOwSSKYJ1cVHQhqK7ZbAiEAus8NxpTRnp5DiTMuyVmhVNPB+bVH -Lhnm4N/QDk5rek0= +MIIBjjCCATWgAwIBAgIUKQSMC66MUw+kPp954ZYOcyKAQDswCgYIKoZIzj0EAwIw +EjEQMA4GA1UECgwHb3RlbC1nbzAeFw0yMjEwMTkwMDA5MTlaFw0yMzEwMTkwMDA5 +MTlaMBIxEDAOBgNVBAoMB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AAQMxN+/vknfXkLcr19qm1L2LK2Eo4wR5X8R6nvedZfnfVgCSabAFIHZ1dEP9uuy +Aud8bntYSnFm5jt09FvG7fcEo2kwZzAdBgNVHQ4EFgQUicGuhnTTkYLZwofXMNLK +SHFeCWgwHwYDVR0jBBgwFoAUicGuhnTTkYLZwofXMNLKSHFeCWgwDwYDVR0TAQH/ +BAUwAwEB/zAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAwRAIg +Lfma8FnnxeSOi6223AsFfYwsNZ2RderNsQrS0PjEHb0CIBkrWacqARUAu7uT4cGu +jVcIxYQqhId5L8p/mAv2PWZS -----END CERTIFICATE----- ` type testOption struct { TestString string + TestBool bool TestDuration time.Duration TestHeaders map[string]string TestURL *url.URL @@ -134,6 +145,56 @@ func TestEnvConfig(t *testing.T) { }, expectedOptions: []testOption{}, }, + { + name: "with a bool config", + reader: EnvOptionsReader{ + GetEnv: func(n string) string { + if n == "HELLO" { + return "true" + } else if n == "WORLD" { + return "false" + } + return "" + }, + }, + configs: []ConfigFn{ + WithBool("HELLO", func(b bool) { + options = append(options, testOption{TestBool: b}) + }), + WithBool("WORLD", func(b bool) { + options = append(options, testOption{TestBool: b}) + }), + }, + expectedOptions: []testOption{ + { + TestBool: true, + }, + { + TestBool: false, + }, + }, + }, + { + name: "with an invalid bool config", + reader: EnvOptionsReader{ + GetEnv: func(n string) string { + if n == "HELLO" { + return "world" + } + return "" + }, + }, + configs: []ConfigFn{ + WithBool("HELLO", func(b bool) { + options = append(options, testOption{TestBool: b}) + }), + }, + expectedOptions: []testOption{ + { + TestBool: false, + }, + }, + }, { name: "with a duration config", reader: EnvOptionsReader{ @@ -265,7 +326,7 @@ func TestEnvConfig(t *testing.T) { } func TestWithTLSConfig(t *testing.T) { - tlsCert, err := createTLSConfig([]byte(WeakCertificate)) + pool, err := createCertPool([]byte(WeakCertificate)) assert.NoError(t, err) reader := EnvOptionsReader{ @@ -285,12 +346,65 @@ func TestWithTLSConfig(t *testing.T) { var option testOption reader.Apply( - WithTLSConfig("CERTIFICATE", func(v *tls.Config) { - option = testOption{TestTLS: v} - })) + WithCertPool("CERTIFICATE", func(cp *x509.CertPool) { + option = testOption{TestTLS: &tls.Config{RootCAs: cp}} + }), + ) // nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool. - assert.Equal(t, tlsCert.RootCAs.Subjects(), option.TestTLS.RootCAs.Subjects()) + assert.Equal(t, pool.Subjects(), option.TestTLS.RootCAs.Subjects()) +} + +func TestWithClientCert(t *testing.T) { + cert, err := tls.X509KeyPair([]byte(WeakCertificate), []byte(WeakKey)) + assert.NoError(t, err) + + reader := EnvOptionsReader{ + GetEnv: func(n string) string { + switch n { + case "CLIENT_CERTIFICATE": + return "/path/tls.crt" + case "CLIENT_KEY": + return "/path/tls.key" + } + return "" + }, + ReadFile: func(n string) ([]byte, error) { + switch n { + case "/path/tls.crt": + return []byte(WeakCertificate), nil + case "/path/tls.key": + return []byte(WeakKey), nil + } + return []byte{}, nil + }, + } + + var option testOption + reader.Apply( + WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { + option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}} + }), + ) + assert.Equal(t, cert, option.TestTLS.Certificates[0]) + + reader.ReadFile = func(s string) ([]byte, error) { return nil, errors.New("oops") } + option.TestTLS = nil + reader.Apply( + WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { + option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}} + }), + ) + assert.Nil(t, option.TestTLS) + + reader.GetEnv = func(s string) string { return "" } + option.TestTLS = nil + reader.Apply( + WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { + option = testOption{TestTLS: &tls.Config{Certificates: []tls.Certificate{c}}} + }), + ) + assert.Nil(t, option.TestTLS) } func TestStringToHeader(t *testing.T) { diff --git a/lightstep/sdk/metric/exporters/otlp/internal/header.go b/lightstep/sdk/metric/exporters/otlp/internal/header.go new file mode 100644 index 00000000..e6a8443e --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/internal/header.go @@ -0,0 +1,25 @@ +// 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 internal contains common functionality for all OTLP exporters. +// This was forked from "go.opentelemetry.io/otel/exporters/otlp/internal". +package internal // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" + +import "go.opentelemetry.io/otel" + +// GetUserAgentHeader return an OTLP header value form "OTel OTLP Exporter Go/{{ .Version }}" +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#user-agent +func GetUserAgentHeader() string { + return "OTel OTLP Exporter Go/" + otel.Version() +} diff --git a/lightstep/sdk/metric/exporters/otlp/options.go b/lightstep/sdk/metric/exporters/otlp/internal/header_test.go similarity index 62% rename from lightstep/sdk/metric/exporters/otlp/options.go rename to lightstep/sdk/metric/exporters/otlp/internal/header_test.go index 1b6c4049..7684cb9c 100644 --- a/lightstep/sdk/metric/exporters/otlp/options.go +++ b/lightstep/sdk/metric/exporters/otlp/internal/header_test.go @@ -12,11 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlp // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp" +// Package internal contains common functionality for all OTLP exporters. +package internal // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" -type config struct{} +import ( + "testing" -// Option are setting options passed to an Exporter on creation. -type Option interface { - apply(config) config + "github.com/stretchr/testify/require" +) + +func TestGetUserAgentHeader(t *testing.T) { + require.Regexp(t, "OTel OTLP Exporter Go/1\\..*", GetUserAgentHeader()) } diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlptest/otlptest.go b/lightstep/sdk/metric/exporters/otlp/internal/otlptest/otlptest.go index aea36e5a..fb2d689c 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlptest/otlptest.go +++ b/lightstep/sdk/metric/exporters/otlp/internal/otlptest/otlptest.go @@ -18,18 +18,11 @@ import ( "math" "time" - collectorpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" commonpb "go.opentelemetry.io/proto/otlp/common/v1" metricspb "go.opentelemetry.io/proto/otlp/metrics/v1" resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" ) -func ExportRequest(rms ...*metricspb.ResourceMetrics) *collectorpb.ExportMetricsServiceRequest { - return &collectorpb.ExportMetricsServiceRequest{ - ResourceMetrics: rms, - } -} - func ResourceMetrics(resource *resourcepb.Resource, schemaURL string, ilms ...*metricspb.ScopeMetrics) *metricspb.ResourceMetrics { return &metricspb.ResourceMetrics{ Resource: resource, diff --git a/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess.go b/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess.go new file mode 100644 index 00000000..da4f5e93 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess.go @@ -0,0 +1,64 @@ +// 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 internal // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" + +import "fmt" + +// PartialSuccess represents the underlying error for all handling +// OTLP partial success messages. Use `errors.Is(err, +// PartialSuccess{})` to test whether an error passed to the OTel +// error handler belongs to this category. +type PartialSuccess struct { + ErrorMessage string + RejectedItems int64 + RejectedKind string +} + +var _ error = PartialSuccess{} + +// Error implements the error interface. +func (ps PartialSuccess) Error() string { + msg := ps.ErrorMessage + if msg == "" { + msg = "empty message" + } + return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind) +} + +// Is supports the errors.Is() interface. +func (ps PartialSuccess) Is(err error) bool { + _, ok := err.(PartialSuccess) + return ok +} + +// TracePartialSuccessError returns an error describing a partial success +// response for the trace signal. +func TracePartialSuccessError(itemsRejected int64, errorMessage string) error { + return PartialSuccess{ + ErrorMessage: errorMessage, + RejectedItems: itemsRejected, + RejectedKind: "spans", + } +} + +// MetricPartialSuccessError returns an error describing a partial success +// response for the metric signal. +func MetricPartialSuccessError(itemsRejected int64, errorMessage string) error { + return PartialSuccess{ + ErrorMessage: errorMessage, + RejectedItems: itemsRejected, + RejectedKind: "metric data points", + } +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess_test.go b/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess_test.go new file mode 100644 index 00000000..46be8469 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/internal/partialsuccess_test.go @@ -0,0 +1,43 @@ +// 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 internal // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" + +import ( + "errors" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func requireErrorString(t *testing.T, expect string, err error) { + t.Helper() + require.NotNil(t, err) + require.Error(t, err) + require.True(t, errors.Is(err, PartialSuccess{})) + + const pfx = "OTLP partial success: " + + msg := err.Error() + require.True(t, strings.HasPrefix(msg, pfx)) + require.Equal(t, expect, msg[len(pfx):]) +} + +func TestPartialSuccessFormat(t *testing.T) { + requireErrorString(t, "empty message (0 metric data points rejected)", MetricPartialSuccessError(0, "")) + requireErrorString(t, "help help (0 metric data points rejected)", MetricPartialSuccessError(0, "help help")) + requireErrorString(t, "what happened (10 metric data points rejected)", MetricPartialSuccessError(10, "what happened")) + requireErrorString(t, "what happened (15 spans rejected)", TracePartialSuccessError(15, "what happened")) +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/client.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/client.go new file mode 100644 index 00000000..33ce35db --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/client.go @@ -0,0 +1,58 @@ +// 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 otlpmetric // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + +import ( + "context" + + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" +) + +// Client handles the transmission of OTLP data to an OTLP receiving endpoint. +type Client interface { + // Temporality returns the Temporality to use for an instrument kind. + Temporality(metric.InstrumentKind) metricdata.Temporality + + // Aggregation returns the Aggregation to use for an instrument kind. + Aggregation(metric.InstrumentKind) aggregation.Aggregation + + // UploadMetrics transmits metric data to an OTLP receiver. + // + // All retry logic must be handled by UploadMetrics alone, the Exporter + // does not implement any retry logic. All returned errors are considered + // unrecoverable. + UploadMetrics(context.Context, *mpb.ResourceMetrics) error + + // ForceFlush flushes any metric data held by an Client. + // + // The deadline or cancellation of the passed context must be honored. An + // appropriate error should be returned in these situations. + ForceFlush(context.Context) error + + // Shutdown flushes all metric data held by a Client and closes any + // connections it holds open. + // + // The deadline or cancellation of the passed context must be honored. An + // appropriate error should be returned in these situations. + // + // Shutdown will only be called once by the Exporter. Once a return value + // is received by the Exporter from Shutdown the Client will not be used + // anymore. Therefore all computational resources need to be released + // after this is called so the Client can be garbage collected. + Shutdown(context.Context) error +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/doc.go similarity index 57% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig_test.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/doc.go index 25021f73..ded169fd 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig_test.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/doc.go @@ -12,4 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig +// Package otlpmetric provides a metric Exporter that can be +// used with PeriodicReader. It transforms metrics data into OTLP and transmits +// the transformed data to OTLP receivers. The Exporter is configurable to use +// different Clients, each using a distinct transport protocol to communicate +// to an OTLP receiving endpoint. +package otlpmetric // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/exporter.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/exporter.go new file mode 100644 index 00000000..ff2fceed --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/exporter.go @@ -0,0 +1,147 @@ +// 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 otlpmetric // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + +import ( + "context" + "fmt" + "sync" + + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/data" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform" +) + +var errShutdown = fmt.Errorf("Exporter is shutdown") + +// Exporter exports metrics data as OTLP. It implements the `PushExporter` interface in github.com/lightstep/otel-launcher-go/lightstep/sdk/metric. +type Exporter struct { + // Ensure synchronous access to the client across all functionality. + clientMu sync.Mutex + client Client + + shutdownOnce sync.Once +} + +func (e *Exporter) String() string { + return "otlp-lightstep" +} + +// Temporality returns the Temporality to use for an instrument kind. +func (e *Exporter) Temporality(k metric.InstrumentKind) metricdata.Temporality { + e.clientMu.Lock() + defer e.clientMu.Unlock() + return e.client.Temporality(k) +} + +// Aggregation returns the Aggregation to use for an instrument kind. +func (e *Exporter) Aggregation(k metric.InstrumentKind) aggregation.Aggregation { + e.clientMu.Lock() + defer e.clientMu.Unlock() + return e.client.Aggregation(k) +} + +// ExportMetrics transforms and transmits metric data to an OTLP receiver. +func (e *Exporter) ExportMetrics(ctx context.Context, rm data.Metrics) error { + otlpRm, err := transform.Metrics(rm) + // Best effort upload of transformable metrics. + e.clientMu.Lock() + upErr := e.client.UploadMetrics(ctx, otlpRm) + e.clientMu.Unlock() + if upErr != nil { + if err == nil { + return upErr + } + // Merge the two errors. + return fmt.Errorf("failed to upload incomplete metrics (%s): %w", err, upErr) + } + return err +} + +// ForceFlushMetrics flushes any metric data held by an Exporter. +func (e *Exporter) ForceFlushMetrics(ctx context.Context, rm data.Metrics) error { + err := e.ExportMetrics(ctx, rm) + if err != nil { + return err + } + // The Exporter does not hold data, forward the command to the client. + e.clientMu.Lock() + defer e.clientMu.Unlock() + return e.client.ForceFlush(ctx) +} + +// ShutdownMetrics flushes all metric data held by an Exporter and releases any held +// computational resources. +func (e *Exporter) ShutdownMetrics(ctx context.Context, rm data.Metrics) error { + err := e.ExportMetrics(ctx, rm) + e.shutdownOnce.Do(func() { + e.clientMu.Lock() + client := e.client + e.client = shutdownClient{ + temporalitySelector: client.Temporality, + aggregationSelector: client.Aggregation, + } + e.clientMu.Unlock() + if stopErr := client.Shutdown(ctx); stopErr != nil && err == errShutdown { + err = stopErr + } else if stopErr != nil { + err = fmt.Errorf("shutdown flush: %w, stop: %v", err, stopErr) + } + }) + return err +} + +// New return an Exporter that uses client to transmits the OTLP data it +// produces. The client is assumed to be fully started and able to communicate +// with its OTLP receiving endpoint. +func New(client Client) *Exporter { + return &Exporter{client: client} +} + +type shutdownClient struct { + temporalitySelector metric.TemporalitySelector + aggregationSelector metric.AggregationSelector +} + +func (c shutdownClient) err(ctx context.Context) error { + if err := ctx.Err(); err != nil { + return err + } + return errShutdown +} + +func (c shutdownClient) Temporality(k metric.InstrumentKind) metricdata.Temporality { + return c.temporalitySelector(k) +} + +func (c shutdownClient) Aggregation(k metric.InstrumentKind) aggregation.Aggregation { + return c.aggregationSelector(k) +} + +func (c shutdownClient) UploadMetrics(ctx context.Context, _ *mpb.ResourceMetrics) error { + return c.err(ctx) +} + +func (c shutdownClient) ForceFlush(ctx context.Context) error { + return c.err(ctx) +} + +func (c shutdownClient) Shutdown(ctx context.Context) error { + return c.err(ctx) +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/envconfig.go similarity index 75% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/envconfig.go index afb6c54a..92505dd2 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/envconfig.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/envconfig.go @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig" +package oconf // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" import ( "crypto/tls" + "crypto/x509" "net/url" "os" "path" @@ -53,6 +54,7 @@ func ApplyHTTPEnvConfigs(cfg Config) Config { func getOptionsFromEnv() []GenericOption { opts := []GenericOption{} + tlsConf := &tls.Config{} DefaultEnvOptionsReader.Apply( envconfig.WithURL("ENDPOINT", func(u *url.URL) { opts = append(opts, withEndpointScheme(u)) @@ -81,8 +83,13 @@ func getOptionsFromEnv() []GenericOption { return cfg }, withEndpointForGRPC(u))) }), - envconfig.WithTLSConfig("CERTIFICATE", func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }), - envconfig.WithTLSConfig("METRICS_CERTIFICATE", func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }), + envconfig.WithCertPool("CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }), + envconfig.WithCertPool("METRICS_CERTIFICATE", func(p *x509.CertPool) { tlsConf.RootCAs = p }), + envconfig.WithClientCert("CLIENT_CERTIFICATE", "CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }), + envconfig.WithClientCert("METRICS_CLIENT_CERTIFICATE", "METRICS_CLIENT_KEY", func(c tls.Certificate) { tlsConf.Certificates = []tls.Certificate{c} }), + envconfig.WithBool("INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }), + envconfig.WithBool("METRICS_INSECURE", func(b bool) { opts = append(opts, withInsecure(b)) }), + withTLSConfig(tlsConf, func(c *tls.Config) { opts = append(opts, WithTLSClientConfig(c)) }), envconfig.WithHeaders("HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }), envconfig.WithHeaders("METRICS_HEADERS", func(h map[string]string) { opts = append(opts, WithHeaders(h)) }), WithEnvCompression("COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }), @@ -108,8 +115,7 @@ func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOpt return func(e *envconfig.EnvOptionsReader) { if v, ok := e.GetEnvValue(n); ok { cp := NoCompression - switch v { - case "gzip": + if v == "gzip" { cp = GzipCompression } @@ -126,3 +132,19 @@ func withEndpointScheme(u *url.URL) GenericOption { return WithSecure() } } + +// revive:disable-next-line:flag-parameter +func withInsecure(b bool) GenericOption { + if b { + return WithInsecure() + } + return WithSecure() +} + +func withTLSConfig(c *tls.Config, fn func(*tls.Config)) func(e *envconfig.EnvOptionsReader) { + return func(e *envconfig.EnvOptionsReader) { + if c.RootCAs != nil || len(c.Certificates) > 0 { + fn(c) + } + } +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options.go similarity index 85% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options.go index 514a4449..5082aa40 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig" +package oconf // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" import ( "crypto/tls" @@ -25,8 +25,12 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/encoding/gzip" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/retry" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/internal/global" ) const ( @@ -57,6 +61,9 @@ type ( // gRPC configurations GRPCCredentials credentials.TransportCredentials + + TemporalitySelector metric.TemporalitySelector + AggregationSelector metric.AggregationSelector } Config struct { @@ -82,6 +89,9 @@ func NewHTTPConfig(opts ...HTTPOption) Config { URLPath: DefaultMetricsPath, Compression: NoCompression, Timeout: DefaultTimeout, + + TemporalitySelector: metric.DefaultTemporalitySelector, + AggregationSelector: metric.DefaultAggregationSelector, }, RetryConfig: retry.DefaultConfig, } @@ -102,8 +112,12 @@ func NewGRPCConfig(opts ...GRPCOption) Config { URLPath: DefaultMetricsPath, Compression: NoCompression, Timeout: DefaultTimeout, + + TemporalitySelector: metric.DefaultTemporalitySelector, + AggregationSelector: metric.DefaultAggregationSelector, }, RetryConfig: retry.DefaultConfig, + DialOptions: []grpc.DialOption{grpc.WithUserAgent(internal.GetUserAgentHeader())}, } cfg = ApplyGRPCEnvConfigs(cfg) for _, opt := range opts { @@ -312,3 +326,32 @@ func WithTimeout(duration time.Duration) GenericOption { return cfg }) } + +func WithTemporalitySelector(selector metric.TemporalitySelector) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Metrics.TemporalitySelector = selector + return cfg + }) +} + +func WithAggregationSelector(selector metric.AggregationSelector) GenericOption { + // Deep copy and validate before using. + wrapped := func(ik metric.InstrumentKind) aggregation.Aggregation { + a := selector(ik) + cpA := a.Copy() + if err := cpA.Err(); err != nil { + cpA = metric.DefaultAggregationSelector(ik) + global.Error( + err, "using default aggregation instead", + "aggregation", a, + "replacement", cpA, + ) + } + return cpA + } + + return newGenericOption(func(cfg Config) Config { + cfg.Metrics.AggregationSelector = wrapped + return cfg + }) +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options_test.go similarity index 65% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options_test.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options_test.go index dfbd9787..b02b90d9 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/options_test.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/options_test.go @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig_test +package oconf_test import ( "errors" + "go.opentelemetry.io/otel/sdk/metric" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/envconfig" - "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" ) const ( @@ -60,29 +62,29 @@ func (f *fileReader) readFile(filename string) ([]byte, error) { if b, ok := (*f)[filename]; ok { return b, nil } - return nil, errors.New("File not found") + return nil, errors.New("file not found") } func TestConfigs(t *testing.T) { - tlsCert, err := otlpconfig.CreateTLSConfig([]byte(WeakCertificate)) + tlsCert, err := oconf.CreateTLSConfig([]byte(WeakCertificate)) assert.NoError(t, err) tests := []struct { name string - opts []otlpconfig.GenericOption + opts []oconf.GenericOption env env fileReader fileReader - asserts func(t *testing.T, c *otlpconfig.Config, grpcOption bool) + asserts func(t *testing.T, c *oconf.Config, grpcOption bool) }{ { name: "Test default configs", - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { assert.Equal(t, "localhost:4317", c.Metrics.Endpoint) } else { assert.Equal(t, "localhost:4318", c.Metrics.Endpoint) } - assert.Equal(t, otlpconfig.NoCompression, c.Metrics.Compression) + assert.Equal(t, oconf.NoCompression, c.Metrics.Compression) assert.Equal(t, map[string]string(nil), c.Metrics.Headers) assert.Equal(t, 10*time.Second, c.Metrics.Timeout) }, @@ -91,10 +93,10 @@ func TestConfigs(t *testing.T) { // Endpoint Tests { name: "Test With Endpoint", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithEndpoint("someendpoint"), + opts: []oconf.GenericOption{ + oconf.WithEndpoint("someendpoint"), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "someendpoint", c.Metrics.Endpoint) }, }, @@ -103,7 +105,7 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "https://env.endpoint/prefix", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.False(t, c.Metrics.Insecure) if grpcOption { assert.Equal(t, "env.endpoint/prefix", c.Metrics.Endpoint) @@ -119,7 +121,7 @@ func TestConfigs(t *testing.T) { "OTEL_EXPORTER_OTLP_ENDPOINT": "https://overrode.by.signal.specific/env/var", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://env.metrics.endpoint", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.True(t, c.Metrics.Insecure) assert.Equal(t, "env.metrics.endpoint", c.Metrics.Endpoint) if !grpcOption { @@ -129,13 +131,13 @@ func TestConfigs(t *testing.T) { }, { name: "Test Mixed Environment and With Endpoint", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithEndpoint("metrics_endpoint"), + opts: []oconf.GenericOption{ + oconf.WithEndpoint("metrics_endpoint"), }, env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "metrics_endpoint", c.Metrics.Endpoint) }, }, @@ -144,7 +146,7 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://env_endpoint", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, true, c.Metrics.Insecure) }, @@ -154,7 +156,7 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": " http://env_endpoint ", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, true, c.Metrics.Insecure) }, @@ -164,7 +166,7 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "https://env_endpoint", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, false, c.Metrics.Insecure) }, @@ -175,7 +177,7 @@ func TestConfigs(t *testing.T) { "OTEL_EXPORTER_OTLP_ENDPOINT": "HTTPS://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "HtTp://env_metrics_endpoint", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) assert.Equal(t, true, c.Metrics.Insecure) }, @@ -184,7 +186,7 @@ func TestConfigs(t *testing.T) { // Certificate tests { name: "Test Default Certificate", - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { @@ -194,10 +196,10 @@ func TestConfigs(t *testing.T) { }, { name: "Test With Certificate", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithTLSClientConfig(tlsCert), + opts: []oconf.GenericOption{ + oconf.WithTLSClientConfig(tlsCert), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { //TODO: make sure gRPC's credentials actually works assert.NotNil(t, c.Metrics.GRPCCredentials) @@ -215,7 +217,7 @@ func TestConfigs(t *testing.T) { fileReader: fileReader{ "cert_path": []byte(WeakCertificate), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { @@ -234,7 +236,7 @@ func TestConfigs(t *testing.T) { "cert_path": []byte(WeakCertificate), "invalid_cert": []byte("invalid certificate file."), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { @@ -245,14 +247,14 @@ func TestConfigs(t *testing.T) { }, { name: "Test Mixed Environment and With Certificate", - opts: []otlpconfig.GenericOption{}, + opts: []oconf.GenericOption{}, env: map[string]string{ "OTEL_EXPORTER_OTLP_CERTIFICATE": "cert_path", }, fileReader: fileReader{ "cert_path": []byte(WeakCertificate), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { @@ -265,17 +267,17 @@ func TestConfigs(t *testing.T) { // Headers tests { name: "Test With Headers", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithHeaders(map[string]string{"h1": "v1"}), + opts: []oconf.GenericOption{ + oconf.WithHeaders(map[string]string{"h1": "v1"}), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1"}, c.Metrics.Headers) }, }, { name: "Test Environment Headers", env: map[string]string{"OTEL_EXPORTER_OTLP_HEADERS": "h1=v1,h2=v2"}, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Metrics.Headers) }, }, @@ -285,17 +287,17 @@ func TestConfigs(t *testing.T) { "OTEL_EXPORTER_OTLP_HEADERS": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_HEADERS": "h1=v1,h2=v2", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Metrics.Headers) }, }, { name: "Test Mixed Environment and With Headers", env: map[string]string{"OTEL_EXPORTER_OTLP_HEADERS": "h1=v1,h2=v2"}, - opts: []otlpconfig.GenericOption{ - otlpconfig.WithHeaders(map[string]string{"m1": "mv1"}), + opts: []oconf.GenericOption{ + oconf.WithHeaders(map[string]string{"m1": "mv1"}), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, map[string]string{"m1": "mv1"}, c.Metrics.Headers) }, }, @@ -303,11 +305,11 @@ func TestConfigs(t *testing.T) { // Compression Tests { name: "Test With Compression", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithCompression(otlpconfig.GzipCompression), + opts: []oconf.GenericOption{ + oconf.WithCompression(oconf.GzipCompression), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, otlpconfig.GzipCompression, c.Metrics.Compression) + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + assert.Equal(t, oconf.GzipCompression, c.Metrics.Compression) }, }, { @@ -315,8 +317,8 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_COMPRESSION": "gzip", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, otlpconfig.GzipCompression, c.Metrics.Compression) + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + assert.Equal(t, oconf.GzipCompression, c.Metrics.Compression) }, }, { @@ -324,30 +326,30 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION": "gzip", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, otlpconfig.GzipCompression, c.Metrics.Compression) + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + assert.Equal(t, oconf.GzipCompression, c.Metrics.Compression) }, }, { name: "Test Mixed Environment and With Compression", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithCompression(otlpconfig.NoCompression), + opts: []oconf.GenericOption{ + oconf.WithCompression(oconf.NoCompression), }, env: map[string]string{ "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION": "gzip", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, otlpconfig.NoCompression, c.Metrics.Compression) + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + assert.Equal(t, oconf.NoCompression, c.Metrics.Compression) }, }, // Timeout Tests { name: "Test With Timeout", - opts: []otlpconfig.GenericOption{ - otlpconfig.WithTimeout(time.Duration(5 * time.Second)), + opts: []oconf.GenericOption{ + oconf.WithTimeout(time.Duration(5 * time.Second)), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, 5*time.Second, c.Metrics.Timeout) }, }, @@ -356,7 +358,7 @@ func TestConfigs(t *testing.T) { env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 15*time.Second) }, }, @@ -366,7 +368,7 @@ func TestConfigs(t *testing.T) { "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 28*time.Second) }, }, @@ -376,48 +378,88 @@ func TestConfigs(t *testing.T) { "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", }, - opts: []otlpconfig.GenericOption{ - otlpconfig.WithTimeout(5 * time.Second), + opts: []oconf.GenericOption{ + oconf.WithTimeout(5 * time.Second), }, - asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 5*time.Second) }, }, + + // Temporality Selector Tests + { + name: "WithTemporalitySelector", + opts: []oconf.GenericOption{ + oconf.WithTemporalitySelector(deltaSelector), + }, + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + // Function value comparisons are disallowed, test non-default + // behavior of a TemporalitySelector here to ensure our "catch + // all" was set. + var undefinedKind metric.InstrumentKind + got := c.Metrics.TemporalitySelector + assert.Equal(t, metricdata.DeltaTemporality, got(undefinedKind)) + }, + }, + + // Aggregation Selector Tests + { + name: "WithAggregationSelector", + opts: []oconf.GenericOption{ + oconf.WithAggregationSelector(dropSelector), + }, + asserts: func(t *testing.T, c *oconf.Config, grpcOption bool) { + // Function value comparisons are disallowed, test non-default + // behavior of a AggregationSelector here to ensure our "catch + // all" was set. + var undefinedKind metric.InstrumentKind + got := c.Metrics.AggregationSelector + assert.Equal(t, aggregation.Drop{}, got(undefinedKind)) + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - origEOR := otlpconfig.DefaultEnvOptionsReader - otlpconfig.DefaultEnvOptionsReader = envconfig.EnvOptionsReader{ + origEOR := oconf.DefaultEnvOptionsReader + oconf.DefaultEnvOptionsReader = envconfig.EnvOptionsReader{ GetEnv: tt.env.getEnv, ReadFile: tt.fileReader.readFile, Namespace: "OTEL_EXPORTER_OTLP", } - t.Cleanup(func() { otlpconfig.DefaultEnvOptionsReader = origEOR }) + t.Cleanup(func() { oconf.DefaultEnvOptionsReader = origEOR }) // Tests Generic options as HTTP Options - cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(tt.opts)...) + cfg := oconf.NewHTTPConfig(asHTTPOptions(tt.opts)...) tt.asserts(t, &cfg, false) // Tests Generic options as gRPC Options - cfg = otlpconfig.NewGRPCConfig(asGRPCOptions(tt.opts)...) + cfg = oconf.NewGRPCConfig(asGRPCOptions(tt.opts)...) tt.asserts(t, &cfg, true) }) } } -func asHTTPOptions(opts []otlpconfig.GenericOption) []otlpconfig.HTTPOption { - converted := make([]otlpconfig.HTTPOption, len(opts)) +func dropSelector(metric.InstrumentKind) aggregation.Aggregation { + return aggregation.Drop{} +} + +func deltaSelector(metric.InstrumentKind) metricdata.Temporality { + return metricdata.DeltaTemporality +} + +func asHTTPOptions(opts []oconf.GenericOption) []oconf.HTTPOption { + converted := make([]oconf.HTTPOption, len(opts)) for i, o := range opts { - converted[i] = otlpconfig.NewHTTPOption(o.ApplyHTTPOption) + converted[i] = oconf.NewHTTPOption(o.ApplyHTTPOption) } return converted } -func asGRPCOptions(opts []otlpconfig.GenericOption) []otlpconfig.GRPCOption { - converted := make([]otlpconfig.GRPCOption, len(opts)) +func asGRPCOptions(opts []oconf.GenericOption) []oconf.GRPCOption { + converted := make([]oconf.GRPCOption, len(opts)) for i, o := range opts { - converted[i] = otlpconfig.NewGRPCOption(o.ApplyGRPCOption) + converted[i] = oconf.NewGRPCOption(o.ApplyGRPCOption) } return converted } diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/optiontypes.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/optiontypes.go similarity index 94% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/optiontypes.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/optiontypes.go index a486e559..e35ec108 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/optiontypes.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/optiontypes.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig" +package oconf // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" import "time" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/tls.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/tls.go similarity index 91% rename from lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/tls.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/tls.go index 7497d702..ed3d5c16 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig/tls.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf/tls.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package otlpconfig // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/otlpconfig" +package oconf // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" import ( "crypto/tls" diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client.go new file mode 100644 index 00000000..0c70dbb8 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client.go @@ -0,0 +1,302 @@ +// 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 otest // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest" + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric/unit" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" + collpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" + cpb "go.opentelemetry.io/proto/otlp/common/v1" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" + rpb "go.opentelemetry.io/proto/otlp/resource/v1" +) + +var ( + // Sat Jan 01 2000 00:00:00 GMT+0000. + start = time.Date(2000, time.January, 01, 0, 0, 0, 0, time.FixedZone("GMT", 0)) + end = start.Add(30 * time.Second) + + kvAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{ + Value: &cpb.AnyValue_StringValue{StringValue: "alice"}, + }} + kvBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{ + Value: &cpb.AnyValue_StringValue{StringValue: "bob"}, + }} + kvSrvName = &cpb.KeyValue{Key: "service.name", Value: &cpb.AnyValue{ + Value: &cpb.AnyValue_StringValue{StringValue: "test server"}, + }} + kvSrvVer = &cpb.KeyValue{Key: "service.version", Value: &cpb.AnyValue{ + Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"}, + }} + + min, max, sum = 2.0, 4.0, 90.0 + hdp = []*mpb.HistogramDataPoint{{ + Attributes: []*cpb.KeyValue{kvAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Count: 30, + Sum: &sum, + ExplicitBounds: []float64{1, 5}, + BucketCounts: []uint64{0, 30, 0}, + Min: &min, + Max: &max, + }} + + hist = &mpb.Histogram{ + AggregationTemporality: mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, + DataPoints: hdp, + } + + dPtsInt64 = []*mpb.NumberDataPoint{ + { + Attributes: []*cpb.KeyValue{kvAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Value: &mpb.NumberDataPoint_AsInt{AsInt: 1}, + }, + { + Attributes: []*cpb.KeyValue{kvBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Value: &mpb.NumberDataPoint_AsInt{AsInt: 2}, + }, + } + dPtsFloat64 = []*mpb.NumberDataPoint{ + { + Attributes: []*cpb.KeyValue{kvAlice}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Value: &mpb.NumberDataPoint_AsDouble{AsDouble: 1.0}, + }, + { + Attributes: []*cpb.KeyValue{kvBob}, + StartTimeUnixNano: uint64(start.UnixNano()), + TimeUnixNano: uint64(end.UnixNano()), + Value: &mpb.NumberDataPoint_AsDouble{AsDouble: 2.0}, + }, + } + + sumInt64 = &mpb.Sum{ + AggregationTemporality: mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, + IsMonotonic: true, + DataPoints: dPtsInt64, + } + sumFloat64 = &mpb.Sum{ + AggregationTemporality: mpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, + IsMonotonic: false, + DataPoints: dPtsFloat64, + } + + gaugeInt64 = &mpb.Gauge{DataPoints: dPtsInt64} + gaugeFloat64 = &mpb.Gauge{DataPoints: dPtsFloat64} + + metrics = []*mpb.Metric{ + { + Name: "int64-gauge", + Description: "Gauge with int64 values", + Unit: string(unit.Dimensionless), + Data: &mpb.Metric_Gauge{Gauge: gaugeInt64}, + }, + { + Name: "float64-gauge", + Description: "Gauge with float64 values", + Unit: string(unit.Dimensionless), + Data: &mpb.Metric_Gauge{Gauge: gaugeFloat64}, + }, + { + Name: "int64-sum", + Description: "Sum with int64 values", + Unit: string(unit.Dimensionless), + Data: &mpb.Metric_Sum{Sum: sumInt64}, + }, + { + Name: "float64-sum", + Description: "Sum with float64 values", + Unit: string(unit.Dimensionless), + Data: &mpb.Metric_Sum{Sum: sumFloat64}, + }, + { + Name: "histogram", + Description: "Histogram", + Unit: string(unit.Dimensionless), + Data: &mpb.Metric_Histogram{Histogram: hist}, + }, + } + + scope = &cpb.InstrumentationScope{ + Name: "test/code/path", + Version: "v0.1.0", + } + scopeMetrics = []*mpb.ScopeMetrics{{ + Scope: scope, + Metrics: metrics, + SchemaUrl: semconv.SchemaURL, + }} + + res = &rpb.Resource{ + Attributes: []*cpb.KeyValue{kvSrvName, kvSrvVer}, + } + resourceMetrics = &mpb.ResourceMetrics{ + Resource: res, + ScopeMetrics: scopeMetrics, + SchemaUrl: semconv.SchemaURL, + } +) + +// ClientFactory is a function that when called returns a +// otlpmetric.Client implementation that is connected to also returned +// Collector implementation. The Client is ready to upload metric data to the +// Collector which is ready to store that data. +// +// If resultCh is not nil, the returned Collector needs to use the responses +// from that channel to send back to the client for every export request. +type ClientFactory func(resultCh <-chan ExportResult) (otlpmetric.Client, Collector) + +// RunClientTests runs a suite of Client integration tests. For example: +// +// t.Run("Integration", RunClientTests(factory)) +func RunClientTests(f ClientFactory) func(*testing.T) { + return func(t *testing.T) { + t.Run("ClientHonorsContextErrors", func(t *testing.T) { + t.Run("ShutdownMetrics", testCtxErrs(func() func(context.Context) error { + c, _ := f(nil) + return c.Shutdown + })) + + t.Run("ForceFlushMetrics", testCtxErrs(func() func(context.Context) error { + c, _ := f(nil) + return c.ForceFlush + })) + + t.Run("UploadMetrics", testCtxErrs(func() func(context.Context) error { + c, _ := f(nil) + return func(ctx context.Context) error { + return c.UploadMetrics(ctx, nil) + } + })) + }) + + t.Run("ForceFlushFlushes", func(t *testing.T) { + ctx := context.Background() + client, collector := f(nil) + require.NoError(t, client.UploadMetrics(ctx, resourceMetrics)) + + require.NoError(t, client.ForceFlush(ctx)) + rm := collector.Collect().Dump() + // Data correctness is not important, just it was received. + require.Greater(t, len(rm), 0, "no data uploaded") + + require.NoError(t, client.Shutdown(ctx)) + rm = collector.Collect().Dump() + assert.Len(t, rm, 0, "client did not flush all data") + }) + + t.Run("UploadMetrics", func(t *testing.T) { + ctx := context.Background() + client, coll := f(nil) + + require.NoError(t, client.UploadMetrics(ctx, resourceMetrics)) + require.NoError(t, client.Shutdown(ctx)) + got := coll.Collect().Dump() + require.Len(t, got, 1, "upload of one ResourceMetrics") + diff := cmp.Diff(got[0], resourceMetrics, cmp.Comparer(proto.Equal)) + if diff != "" { + t.Fatalf("unexpected ResourceMetrics:\n%s", diff) + } + }) + + t.Run("PartialSuccess", func(t *testing.T) { + const n, msg = 2, "bad data" + rCh := make(chan ExportResult, 3) + rCh <- ExportResult{ + Response: &collpb.ExportMetricsServiceResponse{ + PartialSuccess: &collpb.ExportMetricsPartialSuccess{ + RejectedDataPoints: n, + ErrorMessage: msg, + }, + }, + } + rCh <- ExportResult{ + Response: &collpb.ExportMetricsServiceResponse{ + PartialSuccess: &collpb.ExportMetricsPartialSuccess{ + // Should not be logged. + RejectedDataPoints: 0, + ErrorMessage: "", + }, + }, + } + rCh <- ExportResult{ + Response: &collpb.ExportMetricsServiceResponse{}, + } + + ctx := context.Background() + client, _ := f(rCh) + + defer func(orig otel.ErrorHandler) { + otel.SetErrorHandler(orig) + }(otel.GetErrorHandler()) + + errs := []error{} + eh := otel.ErrorHandlerFunc(func(e error) { errs = append(errs, e) }) + otel.SetErrorHandler(eh) + + require.NoError(t, client.UploadMetrics(ctx, resourceMetrics)) + require.NoError(t, client.UploadMetrics(ctx, resourceMetrics)) + require.NoError(t, client.UploadMetrics(ctx, resourceMetrics)) + require.NoError(t, client.Shutdown(ctx)) + + require.Equal(t, 1, len(errs)) + want := fmt.Sprintf("%s (%d metric data points rejected)", msg, n) + assert.ErrorContains(t, errs[0], want) + }) + } +} + +func testCtxErrs(factory func() func(context.Context) error) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + t.Run("DeadlineExceeded", func(t *testing.T) { + innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond) + t.Cleanup(innerCancel) + <-innerCtx.Done() + + f := factory() + assert.ErrorIs(t, f(innerCtx), context.DeadlineExceeded) + }) + + t.Run("Canceled", func(t *testing.T) { + innerCtx, innerCancel := context.WithCancel(ctx) + innerCancel() + + f := factory() + assert.ErrorIs(t, f(innerCtx), context.Canceled) + }) + } +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client_test.go new file mode 100644 index 00000000..dc7d8818 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/client_test.go @@ -0,0 +1,77 @@ +// 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 otest // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest" + +import ( + "context" + "testing" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + cpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" +) + +type client struct { + rCh <-chan ExportResult + storage *Storage +} + +func (c *client) Temporality(k metric.InstrumentKind) metricdata.Temporality { + return metric.DefaultTemporalitySelector(k) +} + +func (c *client) Aggregation(k metric.InstrumentKind) aggregation.Aggregation { + return metric.DefaultAggregationSelector(k) +} + +func (c *client) Collect() *Storage { + return c.storage +} + +func (c *client) UploadMetrics(ctx context.Context, rm *mpb.ResourceMetrics) error { + c.storage.Add(&cpb.ExportMetricsServiceRequest{ + ResourceMetrics: []*mpb.ResourceMetrics{rm}, + }) + if c.rCh != nil { + r := <-c.rCh + if r.Response != nil && r.Response.GetPartialSuccess() != nil { + msg := r.Response.GetPartialSuccess().GetErrorMessage() + n := r.Response.GetPartialSuccess().GetRejectedDataPoints() + if msg != "" || n > 0 { + otel.Handle(internal.MetricPartialSuccessError(n, msg)) + } + } + return r.Err + } + return ctx.Err() +} + +func (c *client) ForceFlush(ctx context.Context) error { return ctx.Err() } +func (c *client) Shutdown(ctx context.Context) error { return ctx.Err() } + +func TestClientTests(t *testing.T) { + factory := func(rCh <-chan ExportResult) (otlpmetric.Client, Collector) { + c := &client{rCh: rCh, storage: NewStorage()} + return c, c + } + + t.Run("Integration", RunClientTests(factory)) +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/collector.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/collector.go new file mode 100644 index 00000000..1b7819b8 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest/collector.go @@ -0,0 +1,444 @@ +// 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 otest // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest" + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/ecdsa" + "crypto/elliptic" + cryptorand "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" // nolint:depguard // This is for testing. + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + mathrand "math/rand" + "net" + "net/http" + "net/url" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" + collpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" +) + +// Collector is the collection target a Client sends metric uploads to. +type Collector interface { + Collect() *Storage +} + +type ExportResult struct { + Response *collpb.ExportMetricsServiceResponse + Err error +} + +// Storage stores uploaded OTLP metric data in their proto form. +type Storage struct { + dataMu sync.Mutex + data []*mpb.ResourceMetrics +} + +// NewStorage returns a configure storage ready to store received requests. +func NewStorage() *Storage { + return &Storage{} +} + +// Add adds the request to the Storage. +func (s *Storage) Add(request *collpb.ExportMetricsServiceRequest) { + s.dataMu.Lock() + defer s.dataMu.Unlock() + s.data = append(s.data, request.ResourceMetrics...) +} + +// Dump returns all added ResourceMetrics and clears the storage. +func (s *Storage) Dump() []*mpb.ResourceMetrics { + s.dataMu.Lock() + defer s.dataMu.Unlock() + + var data []*mpb.ResourceMetrics + data, s.data = s.data, []*mpb.ResourceMetrics{} + return data +} + +// GRPCCollector is an OTLP gRPC server that collects all requests it receives. +type GRPCCollector struct { + collpb.UnimplementedMetricsServiceServer + + headersMu sync.Mutex + headers metadata.MD + storage *Storage + + resultCh <-chan ExportResult + listener net.Listener + srv *grpc.Server +} + +// NewGRPCCollector returns a *GRPCCollector that is listening at the provided +// endpoint. +// +// If endpoint is an empty string, the returned collector will be listeing on +// the localhost interface at an OS chosen port. +// +// If errCh is not nil, the collector will respond to ExportMetrics calls with errors +// sent on that channel. This means that if errCh is not nil ExportMetrics calls will +// block until an error is received. +func NewGRPCCollector(endpoint string, resultCh <-chan ExportResult) (*GRPCCollector, error) { + if endpoint == "" { + endpoint = "localhost:0" + } + + c := &GRPCCollector{ + storage: NewStorage(), + resultCh: resultCh, + } + + var err error + c.listener, err = net.Listen("tcp", endpoint) + if err != nil { + return nil, err + } + + c.srv = grpc.NewServer() + collpb.RegisterMetricsServiceServer(c.srv, c) + go func() { _ = c.srv.Serve(c.listener) }() + + return c, nil +} + +// Shutdown shuts down the gRPC server closing all open connections and +// listeners immediately. +func (c *GRPCCollector) Shutdown() { c.srv.Stop() } + +// Addr returns the net.Addr c is listening at. +func (c *GRPCCollector) Addr() net.Addr { + return c.listener.Addr() +} + +// Collect returns the Storage holding all collected requests. +func (c *GRPCCollector) Collect() *Storage { + return c.storage +} + +// Headers returns the headers received for all requests. +func (c *GRPCCollector) Headers() map[string][]string { + // Makes a copy. + c.headersMu.Lock() + defer c.headersMu.Unlock() + return metadata.Join(c.headers) +} + +// Export handles the export req. +func (c *GRPCCollector) Export(ctx context.Context, req *collpb.ExportMetricsServiceRequest) (*collpb.ExportMetricsServiceResponse, error) { + c.storage.Add(req) + + if h, ok := metadata.FromIncomingContext(ctx); ok { + c.headersMu.Lock() + c.headers = metadata.Join(c.headers, h) + c.headersMu.Unlock() + } + + if c.resultCh != nil { + r := <-c.resultCh + if r.Response == nil { + return &collpb.ExportMetricsServiceResponse{}, r.Err + } + return r.Response, r.Err + } + return &collpb.ExportMetricsServiceResponse{}, nil +} + +var emptyExportMetricsServiceResponse = func() []byte { + body := collpb.ExportMetricsServiceResponse{} + r, err := proto.Marshal(&body) + if err != nil { + panic(err) + } + return r +}() + +type HTTPResponseError struct { + Err error + Status int + Header http.Header +} + +func (e *HTTPResponseError) Error() string { + return fmt.Sprintf("%d: %s", e.Status, e.Err) +} + +func (e *HTTPResponseError) Unwrap() error { return e.Err } + +// HTTPCollector is an OTLP HTTP server that collects all requests it receives. +type HTTPCollector struct { + headersMu sync.Mutex + headers http.Header + storage *Storage + + resultCh <-chan ExportResult + listener net.Listener + srv *http.Server +} + +// NewHTTPCollector returns a *HTTPCollector that is listening at the provided +// endpoint. +// +// If endpoint is an empty string, the returned collector will be listeing on +// the localhost interface at an OS chosen port, not use TLS, and listen at the +// default OTLP metric endpoint path ("/v1/metrics"). If the endpoint contains +// a prefix of "https" the server will generate weak self-signed TLS +// certificates and use them to server data. If the endpoint contains a path, +// that path will be used instead of the default OTLP metric endpoint path. +// +// If errCh is not nil, the collector will respond to HTTP requests with errors +// sent on that channel. This means that if errCh is not nil ExportMetrics calls will +// block until an error is received. +func NewHTTPCollector(endpoint string, resultCh <-chan ExportResult) (*HTTPCollector, error) { + u, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + if u.Host == "" { + u.Host = "localhost:0" + } + if u.Path == "" { + u.Path = oconf.DefaultMetricsPath + } + + c := &HTTPCollector{ + headers: http.Header{}, + storage: NewStorage(), + resultCh: resultCh, + } + + c.listener, err = net.Listen("tcp", u.Host) + if err != nil { + return nil, err + } + + mux := http.NewServeMux() + mux.Handle(u.Path, http.HandlerFunc(c.handler)) + c.srv = &http.Server{Handler: mux} + if u.Scheme == "https" { + cert, err := weakCertificate() + if err != nil { + return nil, err + } + c.srv.TLSConfig = &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + go func() { _ = c.srv.ServeTLS(c.listener, "", "") }() + } else { + go func() { _ = c.srv.Serve(c.listener) }() + } + return c, nil +} + +// Shutdown shuts down the HTTP server closing all open connections and +// listeners. +func (c *HTTPCollector) Shutdown(ctx context.Context) error { + return c.srv.Shutdown(ctx) +} + +// Addr returns the net.Addr c is listening at. +func (c *HTTPCollector) Addr() net.Addr { + return c.listener.Addr() +} + +// Collect returns the Storage holding all collected requests. +func (c *HTTPCollector) Collect() *Storage { + return c.storage +} + +// Headers returns the headers received for all requests. +func (c *HTTPCollector) Headers() map[string][]string { + // Makes a copy. + c.headersMu.Lock() + defer c.headersMu.Unlock() + return c.headers.Clone() +} + +func (c *HTTPCollector) handler(w http.ResponseWriter, r *http.Request) { + c.respond(w, c.record(r)) +} + +func (c *HTTPCollector) record(r *http.Request) ExportResult { + // Currently only supports protobuf. + if v := r.Header.Get("Content-Type"); v != "application/x-protobuf" { + err := fmt.Errorf("content-type not supported: %s", v) + return ExportResult{Err: err} + } + + body, err := c.readBody(r) + if err != nil { + return ExportResult{Err: err} + } + pbRequest := &collpb.ExportMetricsServiceRequest{} + err = proto.Unmarshal(body, pbRequest) + if err != nil { + return ExportResult{ + Err: &HTTPResponseError{ + Err: err, + Status: http.StatusInternalServerError, + }, + } + } + c.storage.Add(pbRequest) + + c.headersMu.Lock() + for k, vals := range r.Header { + for _, v := range vals { + c.headers.Add(k, v) + } + } + c.headersMu.Unlock() + + if c.resultCh != nil { + return <-c.resultCh + } + return ExportResult{Err: err} +} + +func (c *HTTPCollector) readBody(r *http.Request) (body []byte, err error) { + var reader io.ReadCloser + switch r.Header.Get("Content-Encoding") { + case "gzip": + reader, err = gzip.NewReader(r.Body) + if err != nil { + _ = reader.Close() + return nil, &HTTPResponseError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + default: + reader = r.Body + } + + defer func() { + cErr := reader.Close() + if err == nil && cErr != nil { + err = &HTTPResponseError{ + Err: cErr, + Status: http.StatusInternalServerError, + } + } + }() + body, err = io.ReadAll(reader) + if err != nil { + err = &HTTPResponseError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + return body, err +} + +func (c *HTTPCollector) respond(w http.ResponseWriter, resp ExportResult) { + if resp.Err != nil { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("X-Content-Type-Options", "nosniff") + var e *HTTPResponseError + if errors.As(resp.Err, &e) { + for k, vals := range e.Header { + for _, v := range vals { + w.Header().Add(k, v) + } + } + w.WriteHeader(e.Status) + fmt.Fprintln(w, e.Error()) + } else { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, resp.Err.Error()) + } + return + } + + w.Header().Set("Content-Type", "application/x-protobuf") + w.WriteHeader(http.StatusOK) + if resp.Response == nil { + _, _ = w.Write(emptyExportMetricsServiceResponse) + } else { + r, err := proto.Marshal(resp.Response) + if err != nil { + panic(err) + } + _, _ = w.Write(r) + } +} + +type mathRandReader struct{} + +func (mathRandReader) Read(p []byte) (n int, err error) { + return mathrand.Read(p) +} + +var randReader mathRandReader + +// Based on https://golang.org/src/crypto/tls/generate_cert.go, +// simplified and weakened. +func weakCertificate() (tls.Certificate, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), randReader) + if err != nil { + return tls.Certificate{}, err + } + notBefore := time.Now() + notAfter := notBefore.Add(time.Hour) + max := new(big.Int).Lsh(big.NewInt(1), 128) + sn, err := cryptorand.Int(randReader, max) + if err != nil { + return tls.Certificate{}, err + } + tmpl := x509.Certificate{ + SerialNumber: sn, + Subject: pkix.Name{Organization: []string{"otel-go"}}, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"localhost"}, + IPAddresses: []net.IP{net.IPv6loopback, net.IPv4(127, 0, 0, 1)}, + } + derBytes, err := x509.CreateCertificate(randReader, &tmpl, &tmpl, &priv.PublicKey, priv) + if err != nil { + return tls.Certificate{}, err + } + var certBuf bytes.Buffer + err = pem.Encode(&certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + return tls.Certificate{}, err + } + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return tls.Certificate{}, err + } + var privBuf bytes.Buffer + err = pem.Encode(&privBuf, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + if err != nil { + return tls.Certificate{}, err + } + return tls.X509KeyPair(certBuf.Bytes(), privBuf.Bytes()) +} diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute.go similarity index 96% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute.go index 157f98a8..763cf6de 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrictransform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/metrictransform" +package transform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform" import ( "go.opentelemetry.io/otel/attribute" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute_test.go similarity index 99% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute_test.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute_test.go index ce1ceb79..eafe0d19 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/attribute_test.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/attribute_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrictransform +package transform import ( "testing" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric.go similarity index 98% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric.go index ad3b13b6..dcb5b5e1 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric.go @@ -14,7 +14,7 @@ // Package metrictransform provides translations for opentelemetry-go concepts and // structures to otlp structures. -package metrictransform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/metrictransform" +package transform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform" import ( "errors" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric_test.go similarity index 99% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric_test.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric_test.go index ab8ee471..fac94f9c 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/metric_test.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/metric_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrictransform +package transform import ( "math" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource.go similarity index 93% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource.go index 88526696..fe2834aa 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrictransform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/metrictransform" +package transform // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform" import ( "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/aggregator/aggregation" diff --git a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource_test.go similarity index 98% rename from lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource_test.go rename to lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource_test.go index eb7bbef9..b90c047f 100644 --- a/lightstep/sdk/metric/exporters/otlp/internal/metrictransform/resource_test.go +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/transform/resource_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrictransform +package transform import ( "testing" diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go new file mode 100644 index 00000000..59981186 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go @@ -0,0 +1,225 @@ +// 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 otlpmetricgrpc // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" + +import ( + "context" + "time" + + "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/aggregation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" + metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/retry" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" +) + +type client struct { + metadata metadata.MD + exportTimeout time.Duration + requestFunc retry.RequestFunc + + temporalitySelector metric.TemporalitySelector + aggregationSelector metric.AggregationSelector + + // ourConn keeps track of where conn was created: true if created here in + // NewClient, or false if passed with an option. This is important on + // Shutdown as the conn should only be closed if we created it. Otherwise, + // it is up to the processes that passed the conn to close it. + ourConn bool + conn *grpc.ClientConn + msc colmetricpb.MetricsServiceClient +} + +// NewClient creates a new gRPC metric client. +func NewClient(ctx context.Context, options ...Option) (otlpmetric.Client, error) { + cfg := oconf.NewGRPCConfig(asGRPCOptions(options)...) + + c := &client{ + exportTimeout: cfg.Metrics.Timeout, + requestFunc: cfg.RetryConfig.RequestFunc(retryable), + conn: cfg.GRPCConn, + + temporalitySelector: cfg.Metrics.TemporalitySelector, + aggregationSelector: cfg.Metrics.AggregationSelector, + } + + if len(cfg.Metrics.Headers) > 0 { + c.metadata = metadata.New(cfg.Metrics.Headers) + } + + if c.conn == nil { + // If the caller did not provide a ClientConn when the client was + // created, create one using the configuration they did provide. + conn, err := grpc.DialContext(ctx, cfg.Metrics.Endpoint, cfg.DialOptions...) + if err != nil { + return nil, err + } + // Keep track that we own the lifecycle of this conn and need to close + // it on ShutdownMetrics. + c.ourConn = true + c.conn = conn + } + + c.msc = colmetricpb.NewMetricsServiceClient(c.conn) + + return c, nil +} + +// Temporality returns the Temporality to use for an instrument kind. +func (c *client) Temporality(k metric.InstrumentKind) metricdata.Temporality { + return c.temporalitySelector(k) +} + +// Aggregation returns the Aggregation to use for an instrument kind. +func (c *client) Aggregation(k metric.InstrumentKind) aggregation.Aggregation { + return c.aggregationSelector(k) +} + +// ForceFlush does nothing, the client holds no state. +func (c *client) ForceFlush(ctx context.Context) error { return ctx.Err() } + +// Shutdown shuts down the client, freeing all resource. +// +// Any active connections to a remote endpoint are closed if they were created +// by the client. Any gRPC connection passed during creation using +// WithGRPCConn will not be closed. It is the caller's responsibility to +// handle cleanup of that resource. +func (c *client) Shutdown(ctx context.Context) error { + // The otlpmetric.Exporter synchronizes access to client methods and + // ensures this is called only once. The only thing that needs to be done + // here is to release any computational resources the client holds. + + c.metadata = nil + c.requestFunc = nil + c.msc = nil + + err := ctx.Err() + if c.ourConn { + closeErr := c.conn.Close() + // A context timeout error takes precedence over this error. + if err == nil && closeErr != nil { + err = closeErr + } + } + c.conn = nil + return err +} + +// UploadMetrics sends protoMetrics to connected endpoint. +// +// Retryable errors from the server will be handled according to any +// RetryConfig the client was created with. +func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.ResourceMetrics) error { + // The otlpmetric.Exporter synchronizes access to client methods, and + // ensures this is not called after the Exporter is shutdown. Only thing + // to do here is send data. + + select { + case <-ctx.Done(): + // Do not upload if the context is already expired. + return ctx.Err() + default: + } + + ctx, cancel := c.exportContext(ctx) + defer cancel() + + return c.requestFunc(ctx, func(iCtx context.Context) error { + resp, err := c.msc.Export(iCtx, &colmetricpb.ExportMetricsServiceRequest{ + ResourceMetrics: []*metricpb.ResourceMetrics{protoMetrics}, + }) + if resp != nil && resp.PartialSuccess != nil { + msg := resp.PartialSuccess.GetErrorMessage() + n := resp.PartialSuccess.GetRejectedDataPoints() + if n != 0 || msg != "" { + err := internal.MetricPartialSuccessError(n, msg) + otel.Handle(err) + } + } + // nil is converted to OK. + if status.Code(err) == codes.OK { + // Success. + return nil + } + return err + }) +} + +// exportContext returns a copy of parent with an appropriate deadline and +// cancellation function based on the clients configured export timeout. +// +// It is the callers responsibility to cancel the returned context once its +// use is complete, via the parent or directly with the returned CancelFunc, to +// ensure all resources are correctly released. +func (c *client) exportContext(parent context.Context) (context.Context, context.CancelFunc) { + var ( + ctx context.Context + cancel context.CancelFunc + ) + + if c.exportTimeout > 0 { + ctx, cancel = context.WithTimeout(parent, c.exportTimeout) + } else { + ctx, cancel = context.WithCancel(parent) + } + + if c.metadata.Len() > 0 { + ctx = metadata.NewOutgoingContext(ctx, c.metadata) + } + + return ctx, cancel +} + +// retryable returns if err identifies a request that can be retried and a +// duration to wait for if an explicit throttle time is included in err. +func retryable(err error) (bool, time.Duration) { + s := status.Convert(err) + switch s.Code() { + case codes.Canceled, + codes.DeadlineExceeded, + codes.ResourceExhausted, + codes.Aborted, + codes.OutOfRange, + codes.Unavailable, + codes.DataLoss: + return true, throttleDelay(s) + } + + // Not a retry-able error. + return false, 0 +} + +// throttleDelay returns a duration to wait for if an explicit throttle time +// is included in the response status. +func throttleDelay(s *status.Status) time.Duration { + for _, detail := range s.Details() { + if t, ok := detail.(*errdetails.RetryInfo); ok { + return t.RetryDelay.AsDuration() + } + } + return 0 +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go new file mode 100644 index 00000000..fee38160 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go @@ -0,0 +1,142 @@ +// 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 otlpmetricgrpc + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/otest" +) + +func TestThrottleDuration(t *testing.T) { + c := codes.ResourceExhausted + testcases := []struct { + status *status.Status + expected time.Duration + }{ + { + status: status.New(c, "NoRetryInfo"), + expected: 0, + }, + { + status: func() *status.Status { + s, err := status.New(c, "SingleRetryInfo").WithDetails( + &errdetails.RetryInfo{ + RetryDelay: durationpb.New(15 * time.Millisecond), + }, + ) + require.NoError(t, err) + return s + }(), + expected: 15 * time.Millisecond, + }, + { + status: func() *status.Status { + s, err := status.New(c, "ErrorInfo").WithDetails( + &errdetails.ErrorInfo{Reason: "no throttle detail"}, + ) + require.NoError(t, err) + return s + }(), + expected: 0, + }, + { + status: func() *status.Status { + s, err := status.New(c, "ErrorAndRetryInfo").WithDetails( + &errdetails.ErrorInfo{Reason: "with throttle detail"}, + &errdetails.RetryInfo{ + RetryDelay: durationpb.New(13 * time.Minute), + }, + ) + require.NoError(t, err) + return s + }(), + expected: 13 * time.Minute, + }, + { + status: func() *status.Status { + s, err := status.New(c, "DoubleRetryInfo").WithDetails( + &errdetails.RetryInfo{ + RetryDelay: durationpb.New(13 * time.Minute), + }, + &errdetails.RetryInfo{ + RetryDelay: durationpb.New(15 * time.Minute), + }, + ) + require.NoError(t, err) + return s + }(), + expected: 13 * time.Minute, + }, + } + + for _, tc := range testcases { + t.Run(tc.status.Message(), func(t *testing.T) { + require.Equal(t, tc.expected, throttleDelay(tc.status)) + }) + } +} + +func TestRetryable(t *testing.T) { + retryableCodes := map[codes.Code]bool{ + codes.OK: false, + codes.Canceled: true, + codes.Unknown: false, + codes.InvalidArgument: false, + codes.DeadlineExceeded: true, + codes.NotFound: false, + codes.AlreadyExists: false, + codes.PermissionDenied: false, + codes.ResourceExhausted: true, + codes.FailedPrecondition: false, + codes.Aborted: true, + codes.OutOfRange: true, + codes.Unimplemented: false, + codes.Internal: false, + codes.Unavailable: true, + codes.DataLoss: true, + codes.Unauthenticated: false, + } + + for c, want := range retryableCodes { + got, _ := retryable(status.Error(c, "")) + assert.Equalf(t, want, got, "evaluate(%s)", c) + } +} + +func TestClient(t *testing.T) { + factory := func(rCh <-chan otest.ExportResult) (otlpmetric.Client, otest.Collector) { + coll, err := otest.NewGRPCCollector("", rCh) + require.NoError(t, err) + + ctx := context.Background() + addr := coll.Addr().String() + client, err := NewClient(ctx, WithEndpoint(addr), WithInsecure()) + require.NoError(t, err) + return client, coll + } + + t.Run("Integration", otest.RunClientTests(factory)) +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go new file mode 100644 index 00000000..0bb4d542 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go @@ -0,0 +1,257 @@ +// 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 otlpmetricgrpc // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" + +import ( + "fmt" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/internal/retry" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/internal/oconf" +) + +// Option applies a configuration option to the Exporter. +type Option interface { + applyGRPCOption(oconf.Config) oconf.Config +} + +func asGRPCOptions(opts []Option) []oconf.GRPCOption { + converted := make([]oconf.GRPCOption, len(opts)) + for i, o := range opts { + converted[i] = oconf.NewGRPCOption(o.applyGRPCOption) + } + return converted +} + +// RetryConfig defines configuration for retrying the export of metric data +// that failed. +// +// This configuration does not define any network retry strategy. That is +// entirely handled by the gRPC ClientConn. +type RetryConfig retry.Config + +type wrappedOption struct { + oconf.GRPCOption +} + +func (w wrappedOption) applyGRPCOption(cfg oconf.Config) oconf.Config { + return w.ApplyGRPCOption(cfg) +} + +// WithInsecure disables client transport security for the Exporter's gRPC +// connection, just like grpc.WithInsecure() +// (https://pkg.go.dev/google.golang.org/grpc#WithInsecure) does. +// +// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +// environment variable is set, and this option is not passed, that variable +// value will be used to determine client security. If the endpoint has a +// scheme of "http" or "unix" client security will be disabled. If both are +// set, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, client security will be used. +// +// This option has no effect if WithGRPCConn is used. +func WithInsecure() Option { + return wrappedOption{oconf.WithInsecure()} +} + +// WithEndpoint sets the target endpoint the Exporter will connect to. +// +// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +// environment variable is set, and this option is not passed, that variable +// value will be used. If both are set, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT +// will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, "localhost:4317" will be used. +// +// This option has no effect if WithGRPCConn is used. +func WithEndpoint(endpoint string) Option { + return wrappedOption{oconf.WithEndpoint(endpoint)} +} + +// WithReconnectionPeriod set the minimum amount of time between connection +// attempts to the target endpoint. +// +// This option has no effect if WithGRPCConn is used. +func WithReconnectionPeriod(rp time.Duration) Option { + return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config { + cfg.ReconnectionPeriod = rp + return cfg + })} +} + +func compressorToCompression(compressor string) oconf.Compression { + if compressor == "gzip" { + return oconf.GzipCompression + } + + otel.Handle(fmt.Errorf("invalid compression type: '%s', using no compression as default", compressor)) + return oconf.NoCompression +} + +// WithCompressor sets the compressor the gRPC client uses. +// +// It is the responsibility of the caller to ensure that the compressor set +// has been registered with google.golang.org/grpc/encoding (see +// encoding.RegisterCompressor for more information). For example, to register +// the gzip compressor import the package: +// +// import _ "google.golang.org/grpc/encoding/gzip" +// +// If the OTEL_EXPORTER_OTLP_COMPRESSION or +// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION environment variable is set, and +// this option is not passed, that variable value will be used. That value can +// be either "none" or "gzip". If both are set, +// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, no compressor will be used. +// +// This option has no effect if WithGRPCConn is used. +func WithCompressor(compressor string) Option { + return wrappedOption{oconf.WithCompression(compressorToCompression(compressor))} +} + +// WithHeaders will send the provided headers with each gRPC requests. +// +// If the OTEL_EXPORTER_OTLP_HEADERS or OTEL_EXPORTER_OTLP_METRICS_HEADERS +// environment variable is set, and this option is not passed, that variable +// value will be used. The value will be parsed as a list of key value pairs. +// These pairs are expected to be in the W3C Correlation-Context format +// without additional semi-colon delimited metadata (i.e. "k1=v1,k2=v2"). If +// both are set, OTEL_EXPORTER_OTLP_METRICS_HEADERS will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, no user headers will be set. +func WithHeaders(headers map[string]string) Option { + return wrappedOption{oconf.WithHeaders(headers)} +} + +// WithTLSCredentials sets the gRPC connection to use creds. +// +// If the OTEL_EXPORTER_OTLP_CERTIFICATE or +// OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE environment variable is set, and +// this option is not passed, that variable value will be used. The value will +// be parsed the filepath of the TLS certificate chain to use. If both are +// set, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, no TLS credentials will be used. +// +// This option has no effect if WithGRPCConn is used. +func WithTLSCredentials(creds credentials.TransportCredentials) Option { + return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config { + cfg.Metrics.GRPCCredentials = creds + return cfg + })} +} + +// WithServiceConfig defines the default gRPC service config used. +// +// This option has no effect if WithGRPCConn is used. +func WithServiceConfig(serviceConfig string) Option { + return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config { + cfg.ServiceConfig = serviceConfig + return cfg + })} +} + +// WithDialOption sets explicit grpc.DialOptions to use when establishing a +// gRPC connection. The options here are appended to the internal grpc.DialOptions +// used so they will take precedence over any other internal grpc.DialOptions +// they might conflict with. +// +// This option has no effect if WithGRPCConn is used. +func WithDialOption(opts ...grpc.DialOption) Option { + return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config { + cfg.DialOptions = opts + return cfg + })} +} + +// WithGRPCConn sets conn as the gRPC ClientConn used for all communication. +// +// This option takes precedence over any other option that relates to +// establishing or persisting a gRPC connection to a target endpoint. Any +// other option of those types passed will be ignored. +// +// It is the callers responsibility to close the passed conn. The Exporter +// ShutdownMetrics method will not close this connection. +func WithGRPCConn(conn *grpc.ClientConn) Option { + return wrappedOption{oconf.NewGRPCOption(func(cfg oconf.Config) oconf.Config { + cfg.GRPCConn = conn + return cfg + })} +} + +// WithTimeout sets the max amount of time an Exporter will attempt an export. +// +// This takes precedence over any retry settings defined by WithRetry. Once +// this time limit has been reached the export is abandoned and the metric +// data is dropped. +// +// If the OTEL_EXPORTER_OTLP_TIMEOUT or OTEL_EXPORTER_OTLP_METRICS_TIMEOUT +// environment variable is set, and this option is not passed, that variable +// value will be used. The value will be parsed as an integer representing the +// timeout in milliseconds. If both are set, +// OTEL_EXPORTER_OTLP_METRICS_TIMEOUT will take precedence. +// +// By default, if an environment variable is not set, and this option is not +// passed, a timeout of 10 seconds will be used. +func WithTimeout(duration time.Duration) Option { + return wrappedOption{oconf.WithTimeout(duration)} +} + +// WithRetry sets the retry policy for transient retryable errors that are +// returned by the target endpoint. +// +// If the target endpoint responds with not only a retryable error, but +// explicitly returns a backoff time in the response, that time will take +// precedence over these settings. +// +// These settings do not define any network retry strategy. That is entirely +// handled by the gRPC ClientConn. +// +// If unset, the default retry policy will be used. It will retry the export +// 5 seconds after receiving a retryable error and increase exponentially +// after each error for no more than a total time of 1 minute. +func WithRetry(settings RetryConfig) Option { + return wrappedOption{oconf.WithRetry(retry.Config(settings))} +} + +// WithTemporalitySelector sets the TemporalitySelector the client will use to +// determine the Temporality of an instrument based on its kind. If this option +// is not used, the client will use the DefaultTemporalitySelector from the +// go.opentelemetry.io/otel/sdk/metric package. +func WithTemporalitySelector(selector metric.TemporalitySelector) Option { + return wrappedOption{oconf.WithTemporalitySelector(selector)} +} + +// WithAggregationSelector sets the AggregationSelector the client will use to +// determine the aggregation to use for an instrument based on its kind. If +// this option is not used, the reader will use the DefaultAggregationSelector +// from the go.opentelemetry.io/otel/sdk/metric package, or the aggregation +// explicitly passed for a view matching an instrument. +func WithAggregationSelector(selector metric.AggregationSelector) Option { + return wrappedOption{oconf.WithAggregationSelector(selector)} +} diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go new file mode 100644 index 00000000..a09db7a5 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go @@ -0,0 +1,20 @@ +// 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 otlpmetricgrpc provides an otlpmetric.Client that communicates +// with an OTLP receiving endpoint using gRPC. This is a fork of the client +// code in "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc", +// except the `NewClient()` function is exported, which allows us to use it +// from the otlpmetric.Exporter. +package otlpmetricgrpc // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" diff --git a/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go new file mode 100644 index 00000000..f7630760 --- /dev/null +++ b/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go @@ -0,0 +1,42 @@ +// 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 otlpmetricgrpc_test + +import ( + "context" + + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/sdk/metric" +) + +func Example() { + ctx := context.Background() + exp, err := otlpmetricgrpc.New(ctx) + if err != nil { + panic(err) + } + + meterProvider := metric.NewMeterProvider(metric.WithReader(metric.NewPeriodicReader(exp))) + defer func() { + if err := meterProvider.Shutdown(ctx); err != nil { + panic(err) + } + }() + global.SetMeterProvider(meterProvider) + + // From here, the meterProvider can be used by instrumentation to collect + // telemetry. +} diff --git a/lightstep/sdk/metric/go.mod b/lightstep/sdk/metric/go.mod index 06509ac5..51618544 100644 --- a/lightstep/sdk/metric/go.mod +++ b/lightstep/sdk/metric/go.mod @@ -3,35 +3,39 @@ module github.com/lightstep/otel-launcher-go/lightstep/sdk/metric go 1.18 require ( - github.com/cenkalti/backoff/v4 v4.1.3 + github.com/cenkalti/backoff/v4 v4.2.0 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 + github.com/go-logr/logr v1.2.3 + github.com/go-logr/stdr v1.2.2 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.8 + github.com/google/go-cmp v0.5.9 github.com/lightstep/go-expohisto v1.0.0 - github.com/stretchr/testify v1.8.0 - go.opentelemetry.io/otel v1.10.0 - go.opentelemetry.io/otel/metric v0.31.0 - go.opentelemetry.io/otel/sdk v1.10.0 + github.com/stretchr/testify v1.8.1 + go.opentelemetry.io/otel v1.11.2 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 + go.opentelemetry.io/otel/metric v0.34.0 + go.opentelemetry.io/otel/sdk v1.11.2 + go.opentelemetry.io/otel/sdk/metric v0.34.0 go.opentelemetry.io/proto/otlp v0.19.0 go.uber.org/multierr v1.8.0 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/grpc v1.46.0 - google.golang.org/protobuf v1.28.0 + google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 + google.golang.org/grpc v1.51.0 + google.golang.org/protobuf v1.28.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/otel/trace v1.10.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 // indirect + go.opentelemetry.io/otel/trace v1.11.2 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect - golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + golang.org/x/text v0.4.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/lightstep/sdk/metric/go.sum b/lightstep/sdk/metric/go.sum index 1929e139..49416b6a 100644 --- a/lightstep/sdk/metric/go.sum +++ b/lightstep/sdk/metric/go.sum @@ -35,8 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -50,7 +50,6 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/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= @@ -63,7 +62,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m 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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -117,8 +115,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -157,13 +155,15 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -173,14 +173,22 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= -go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= -go.opentelemetry.io/otel/metric v0.31.0 h1:6SiklT+gfWAwWUR0meEMxQBtihpiEs4c+vL9spDTqUs= -go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= -go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= -go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= -go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E= -go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= +go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= +go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 h1:htgM8vZIF8oPSCxa341e3IZ4yr/sKxgu8KZYllByiVY= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2/go.mod h1:rqbht/LlhVBgn5+k3M5QK96K5Xb0DvXpMJ5SFQpY6uw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 h1:kpskzLZ60cJ48SJ4uxWa6waBL+4kSV6nVK8rP+QM8Wg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0/go.mod h1:4+x3i62TEegDHuzNva0bMcAN8oUi5w4liGb1d/VgPYo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 h1:e7kFb4pJLbhJgAwUdoVTHzB9pGujs5O8/7gFyZL88fg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0/go.mod h1:3x00m9exjIbhK+zTO4MsCSlfbVmgvLP0wjDgDKa/8bw= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= +go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= +go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= +go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= +go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= +go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= +go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -250,10 +258,9 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -295,13 +302,11 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -309,8 +314,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -430,8 +435,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp 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/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 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= @@ -445,8 +450,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/lightstep/sdk/metric/internal/global/internal_logging.go b/lightstep/sdk/metric/internal/global/internal_logging.go new file mode 100644 index 00000000..15b98cd4 --- /dev/null +++ b/lightstep/sdk/metric/internal/global/internal_logging.go @@ -0,0 +1,63 @@ +// 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 global // import "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/internal/global" + +import ( + "log" + "os" + "sync" + + "github.com/go-logr/logr" + "github.com/go-logr/stdr" +) + +// globalLogger is the logging interface used within the otel api and sdk provide deatails of the internals. +// +// The default logger uses stdr which is backed by the standard `log.Logger` +// interface. This logger will only show messages at the Error Level. +var globalLogger logr.Logger = stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)) +var globalLoggerLock = &sync.RWMutex{} + +// SetLogger overrides the globalLogger with l. +// +// To see Info messages use a logger with `l.V(1).Enabled() == true` +// To see Debug messages use a logger with `l.V(5).Enabled() == true`. +func SetLogger(l logr.Logger) { + globalLoggerLock.Lock() + defer globalLoggerLock.Unlock() + globalLogger = l +} + +// Info prints messages about the general state of the API or SDK. +// This should usually be less then 5 messages a minute. +func Info(msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.V(1).Info(msg, keysAndValues...) +} + +// Error prints messages about exceptional states of the API or SDK. +func Error(err error, msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.Error(err, msg, keysAndValues...) +} + +// Debug prints messages about all internal changes in the API or SDK. +func Debug(msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.V(5).Info(msg, keysAndValues...) +} diff --git a/lightstep/sdk/metric/internal/syncstate/sync.go b/lightstep/sdk/metric/internal/syncstate/sync.go index 464da723..c5f0233b 100644 --- a/lightstep/sdk/metric/internal/syncstate/sync.go +++ b/lightstep/sdk/metric/internal/syncstate/sync.go @@ -301,6 +301,9 @@ func attributesEqual(a, b []attribute.KeyValue) bool { return false } for i := range a { + if a[i].Value.Type() != b[i].Value.Type() { + return false + } if a[i].Key != b[i].Key { return false } diff --git a/pipelines/common.go b/pipelines/common.go index b9a4a4f8..62f51701 100644 --- a/pipelines/common.go +++ b/pipelines/common.go @@ -15,9 +15,10 @@ package pipelines import ( + oldotlpmetricgrpc "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "google.golang.org/grpc/credentials" - "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/sdk/resource" ) @@ -63,15 +64,17 @@ type PipelineConfig struct { type PipelineSetupFunc func(PipelineConfig) (func() error, error) -func (p PipelineConfig) secureMetricOption() otlpmetricgrpc.Option { +func (p PipelineConfig) secureMetricOption() (otlpmetricgrpc.Option, oldotlpmetricgrpc.Option) { if p.Insecure { - return otlpmetricgrpc.WithInsecure() + return otlpmetricgrpc.WithInsecure(), oldotlpmetricgrpc.WithInsecure() } else if p.Credentials != nil { - return otlpmetricgrpc.WithTLSCredentials(p.Credentials) + return otlpmetricgrpc.WithTLSCredentials(p.Credentials), oldotlpmetricgrpc.WithTLSCredentials(p.Credentials) } return otlpmetricgrpc.WithTLSCredentials( - credentials.NewClientTLSFromCert(nil, ""), - ) + credentials.NewClientTLSFromCert(nil, ""), + ), oldotlpmetricgrpc.WithTLSCredentials( + credentials.NewClientTLSFromCert(nil, ""), + ) } func (p PipelineConfig) secureTraceOption() otlptracegrpc.Option { diff --git a/pipelines/go.mod b/pipelines/go.mod index 10273893..90e00fcf 100644 --- a/pipelines/go.mod +++ b/pipelines/go.mod @@ -11,25 +11,23 @@ require ( go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4 // b3 and opentracing propagators - go.opentelemetry.io/contrib/propagators/b3 v1.11.1 - go.opentelemetry.io/contrib/propagators/ot v1.11.1 - go.opentelemetry.io/otel v1.11.1 - - // Standard metric gRPC OTLP exporter - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 + go.opentelemetry.io/contrib/propagators/b3 v1.12.0 + go.opentelemetry.io/contrib/propagators/ot v1.12.0 + go.opentelemetry.io/otel v1.11.2 // Standard trace SDK and gRPC OTLP exporter - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 - go.opentelemetry.io/otel/metric v0.33.0 - go.opentelemetry.io/otel/sdk v1.11.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 + go.opentelemetry.io/otel/metric v0.34.0 + go.opentelemetry.io/otel/sdk v1.11.2 + go.opentelemetry.io/otel/sdk/metric v0.34.0 // gRPC - google.golang.org/grpc v1.50.1 + google.golang.org/grpc v1.51.0 ) require ( - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -38,25 +36,27 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/shirou/gopsutil/v3 v3.22.9 // indirect - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 - go.opentelemetry.io/otel/sdk/metric v0.31.0 - go.opentelemetry.io/otel/trace v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 // indirect + go.opentelemetry.io/otel/trace v1.11.2 // indirect go.opentelemetry.io/proto/otlp v0.19.0 go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.4.0 // indirect google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 // indirect google.golang.org/protobuf v1.28.1 ) -require github.com/lightstep/otel-launcher-go/lightstep/instrumentation v1.11.1 +require ( + github.com/lightstep/otel-launcher-go/lightstep/instrumentation v1.11.1 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pipelines/go.sum b/pipelines/go.sum index 99efa205..c67a5d2a 100644 --- a/pipelines/go.sum +++ b/pipelines/go.sum @@ -35,9 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -162,13 +161,15 @@ github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmS 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= @@ -187,30 +188,30 @@ go.opentelemetry.io/contrib/instrumentation/host v0.36.4 h1:2D0q/69KewnkCkOI9I9u go.opentelemetry.io/contrib/instrumentation/host v0.36.4/go.mod h1:IQdse+GFHec/g2M4wtj6cE4uA5PJGQjjXP/602LjHBQ= go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4 h1:7AY5NdRzyU5s1ek3E4VK3FBnPtQ6La1i7sIn9hNgjsk= go.opentelemetry.io/contrib/instrumentation/runtime v0.36.4/go.mod h1:yFSLOnffweT7Es+IzY1DF5KP0xa2Wl15SJfKqAyDXq8= -go.opentelemetry.io/contrib/propagators/b3 v1.11.1 h1:icQ6ttRV+r/2fnU46BIo/g/mPu6Rs5Ug8Rtohe3KqzI= -go.opentelemetry.io/contrib/propagators/b3 v1.11.1/go.mod h1:ECIveyMXgnl4gorxFcA7RYjJY/Ql9n20ubhbfDc3QfA= -go.opentelemetry.io/contrib/propagators/ot v1.11.1 h1:iezQwYW2sAaXwbXXA6Zg+PLjNnzc+M4hLKvOR6Q/CvI= -go.opentelemetry.io/contrib/propagators/ot v1.11.1/go.mod h1:oBced35DewKV7xvvIWC/oCaCFvthvTa6zjyvP2JhPAY= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 h1:H0+xwv4shKw0gfj/ZqR13qO2N/dBQogB1OcRjJjV39Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0/go.mod h1:nkenGD8vcvs0uN6WhR90ZVHQlgDsRmXicnNadMnk+XQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 h1:BaQ2xM5cPmldVCMvbLoy5tcLUhXCtIhItDYBNw83B7Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0/go.mod h1:VRr8tlXQEsTdesDCh0qBe2iKDWhpi3ZqDYw6VlZ8MhI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 h1:MEQNafcNCB0uQIti/oHgU7CZpUMYQ7qigBwMVKycHvc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1/go.mod h1:19O5I2U5iys38SsmT2uDJja/300woyzE1KPIQxEUBUc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 h1:LYyG/f1W/jzAix16jbksJfMQFpOH/Ma6T639pVPMgfI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1/go.mod h1:QrRRQiY3kzAoYPNLP0W/Ikg0gR6V3LMc+ODSxr7yyvg= -go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= -go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/sdk/metric v0.31.0 h1:2sZx4R43ZMhJdteKAlKoHvRgrMp53V1aRxvEf5lCq8Q= -go.opentelemetry.io/otel/sdk/metric v0.31.0/go.mod h1:fl0SmNnX9mN9xgU6OLYLMBMrNAsaZQi7qBwprwO3abk= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/contrib/propagators/b3 v1.12.0 h1:OtfTF8bneN8qTeo/j92kcvc0iDDm4bm/c3RzaUJfiu0= +go.opentelemetry.io/contrib/propagators/b3 v1.12.0/go.mod h1:0JDB4elfPUWGsCH/qhaMkDzP1l8nB0ANVx8zXuAYEwg= +go.opentelemetry.io/contrib/propagators/ot v1.12.0 h1:6yNXAAQW08uIwgQKQvGC/0/5KkUCZmOcwSjj8zYg1ZU= +go.opentelemetry.io/contrib/propagators/ot v1.12.0/go.mod h1:t2n1RTxGm14/AEMSELd0jJo3NBjeEHnDtCRYXKTl0Ak= +go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= +go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 h1:htgM8vZIF8oPSCxa341e3IZ4yr/sKxgu8KZYllByiVY= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2/go.mod h1:rqbht/LlhVBgn5+k3M5QK96K5Xb0DvXpMJ5SFQpY6uw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0 h1:kpskzLZ60cJ48SJ4uxWa6waBL+4kSV6nVK8rP+QM8Wg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.34.0/go.mod h1:4+x3i62TEegDHuzNva0bMcAN8oUi5w4liGb1d/VgPYo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0 h1:e7kFb4pJLbhJgAwUdoVTHzB9pGujs5O8/7gFyZL88fg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.34.0/go.mod h1:3x00m9exjIbhK+zTO4MsCSlfbVmgvLP0wjDgDKa/8bw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 h1:fqR1kli93643au1RKo0Uma3d2aPQKT+WBKfTSBaKbOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2/go.mod h1:5Qn6qvgkMsLDX+sYK64rHb1FPhpn0UtxF+ouX1uhyJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 h1:ERwKPn9Aer7Gxsc0+ZlutlH1bEEAUXAUhqm3Y45ABbk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2/go.mod h1:jWZUM2MWhWCJ9J9xVbRx7tzK1mXKpAlze4CeulycwVY= +go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= +go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= +go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= +go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= +go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= +go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= +go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= +go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -281,8 +282,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= -golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -339,8 +340,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -459,8 +460,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp 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/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 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= diff --git a/pipelines/metrics.go b/pipelines/metrics.go index 1653bc54..3c05c748 100644 --- a/pipelines/metrics.go +++ b/pipelines/metrics.go @@ -25,7 +25,8 @@ import ( // The Lightstep SDK sdkmetric "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric" "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/aggregator/aggregation" - otlpmetric "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric" + "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/exporters/otlp/otlpmetric/otlpmetricgrpc" "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/sdkinstrument" "github.com/lightstep/otel-launcher-go/lightstep/sdk/metric/view" @@ -39,17 +40,13 @@ import ( contribRuntime "go.opentelemetry.io/contrib/instrumentation/runtime" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/metric" metricglobal "go.opentelemetry.io/otel/metric/global" - // The old Metrics SDK - oldotlpmetric "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - oldaggregation "go.opentelemetry.io/otel/sdk/metric/export/aggregation" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" - + // The otel Metrics SDK + otelotlpmetricgrpc "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + otelsdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/metadata" @@ -120,14 +117,15 @@ func NewMetricsPipeline(c PipelineConfig) (func() error, error) { var provider metric.MeterProvider var shutdown func() error - newPref, oldPref, err := tempoOptions(c) + lsPref, otelPref, err := tempoOptions(c) + lsSecure, otelSecure := c.secureMetricOption() if err != nil { return nil, fmt.Errorf("invalid metric view configuration: %v", err) } if c.UseLightstepMetricsSDK { // Install the Lightstep metrics SDK - metricExporter, err := c.newMetricsExporter() + metricExporter, err := c.newMetricsExporter(lsSecure) if err != nil { return nil, fmt.Errorf("failed to create metric exporter: %v", err) } @@ -136,7 +134,7 @@ func NewMetricsPipeline(c PipelineConfig) (func() error, error) { sdkmetric.WithResource(c.Resource), sdkmetric.WithReader( sdkmetric.NewPeriodicReader(metricExporter, period), - newPref, + lsPref, ), ) @@ -147,27 +145,21 @@ func NewMetricsPipeline(c PipelineConfig) (func() error, error) { } else { // Install the OTel-Go community metrics SDK. - metricExporter, err := c.newOldMetricsExporter(oldPref) + metricExporter, err := c.newOtelMetricsExporter(otelPref, otelSecure) if err != nil { return nil, fmt.Errorf("failed to create metric exporter: %v", err) } - sdk := controller.New( - processor.NewFactory( - selector.NewWithHistogramDistribution(), + meterProvider := otelsdkmetric.NewMeterProvider( + otelsdkmetric.WithResource(c.Resource), + otelsdkmetric.WithReader(otelsdkmetric.NewPeriodicReader( metricExporter, - ), - controller.WithExporter(metricExporter), - controller.WithResource(c.Resource), - controller.WithCollectPeriod(period), + otelsdkmetric.WithInterval(period), + )), ) - - if err = sdk.Start(context.Background()); err != nil { - return nil, fmt.Errorf("failed to start controller: %v", err) - } - - provider = sdk + metricglobal.SetMeterProvider(meterProvider) + provider = meterProvider shutdown = func() error { - return sdk.Stop(context.Background()) + return meterProvider.Shutdown(context.Background()) } } @@ -272,9 +264,10 @@ func interceptor( return err } -func (c PipelineConfig) newClient() otlpmetric.Client { +func (c PipelineConfig) newClient(secure otlpmetricgrpc.Option) (otlpmetric.Client, error) { return otlpmetricgrpc.NewClient( - c.secureMetricOption(), + context.Background(), + secure, otlpmetricgrpc.WithEndpoint(c.Endpoint), otlpmetricgrpc.WithHeaders(c.Headers), otlpmetricgrpc.WithCompressor(gzip.Name), @@ -284,25 +277,32 @@ func (c PipelineConfig) newClient() otlpmetric.Client { ) } -func (c PipelineConfig) newMetricsExporter() (*otlpmetric.Exporter, error) { - return otlpmetric.New( - context.Background(), - c.newClient(), - ) +func (c PipelineConfig) newMetricsExporter(secure otlpmetricgrpc.Option) (*otlpmetric.Exporter, error) { + client, err := c.newClient(secure) + if err != nil { + return nil, err + } + return otlpmetric.New(client), nil } -func (c PipelineConfig) newOldMetricsExporter(tempo oldaggregation.TemporalitySelector) (*oldotlpmetric.Exporter, error) { - return oldotlpmetric.New( +func (c PipelineConfig) newOtelMetricsExporter(temporality otelsdkmetric.TemporalitySelector, secureOpt otelotlpmetricgrpc.Option) (otelsdkmetric.Exporter, error) { + return otelotlpmetricgrpc.New( context.Background(), - c.newClient(), - oldotlpmetric.WithMetricAggregationTemporalitySelector(tempo), + secureOpt, + otelotlpmetricgrpc.WithTemporalitySelector(temporality), + otelotlpmetricgrpc.WithEndpoint(c.Endpoint), + otelotlpmetricgrpc.WithHeaders(c.Headers), + otelotlpmetricgrpc.WithCompressor(gzip.Name), + otelotlpmetricgrpc.WithDialOption( + grpc.WithUnaryInterceptor(interceptor), + ), ) } -func tempoOptions(c PipelineConfig) (view.Option, oldaggregation.TemporalitySelector, error) { +func tempoOptions(c PipelineConfig) (view.Option, otelsdkmetric.TemporalitySelector, error) { syncPref := aggregation.CumulativeTemporality asyncPref := aggregation.CumulativeTemporality - var oldSelector oldaggregation.TemporalitySelector + var otelSelector otelsdkmetric.TemporalitySelector switch lower := strings.ToLower(c.TemporalityPreference); lower { case "delta": @@ -318,15 +318,21 @@ func tempoOptions(c PipelineConfig) (view.Option, oldaggregation.TemporalitySele // preference setting. We WILL NOT FIX this defect. // Instead, as otel-launcher-go v1.10.x will use the // Lightstep metrics SDK by default. - oldSelector = oldaggregation.DeltaTemporalitySelector() + otelSelector = func(otelsdkmetric.InstrumentKind) metricdata.Temporality { + return metricdata.DeltaTemporality + } case "stateless": // asyncPref set above. syncPref = aggregation.DeltaTemporality - oldSelector = oldaggregation.StatelessTemporalitySelector() + otelSelector = func(otelsdkmetric.InstrumentKind) metricdata.Temporality { + return metricdata.DeltaTemporality + } case "", "cumulative": // syncPref, asyncPref set above. - oldSelector = oldaggregation.CumulativeTemporalitySelector() + otelSelector = func(otelsdkmetric.InstrumentKind) metricdata.Temporality { + return metricdata.CumulativeTemporality + } default: return nil, nil, fmt.Errorf("invalid temporality preference: %v", c.TemporalityPreference) @@ -342,5 +348,5 @@ func tempoOptions(c PipelineConfig) (view.Option, oldaggregation.TemporalitySele return asyncPref } }, - ), oldSelector, nil + ), otelSelector, nil }