pax_global_header00006660000000000000000000000064141422674400014516gustar00rootroot0000000000000052 comment=f352383666cc2c480a8320c12e64a2472cfd426e golang-opentelemetry-otel-1.1.0/000077500000000000000000000000001414226744000166175ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/.gitattributes000066400000000000000000000001311414226744000215050ustar00rootroot00000000000000* text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf golang-opentelemetry-otel-1.1.0/.github/000077500000000000000000000000001414226744000201575ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001414226744000223425ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000007761414226744000250460ustar00rootroot00000000000000--- name: Bug report about: Create a report of invalid behavior to help us improve title: '' labels: bug assignees: '' --- ### Description A clear and concise description of what the bug is. ### Environment - OS: [e.g. iOS] - Architecture: [e.g. x86, i386] - Go Version: [e.g. 1.15] - opentelemetry-go version: [e.g. v0.14.0, 3c7face] ### Steps To Reproduce 1. Use the configuration '...' 2. Run '...' 3. See error ### Expected behavior A clear and concise description of what you expected to happen. golang-opentelemetry-otel-1.1.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012321414226744000260650ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- ### Problem Statement A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] ### Proposed Solution A clear and concise description of what you want to happen. #### Alternatives A clear and concise description of any alternative solutions or features you've considered. #### Prior Art A clear and concise list of any similar and existing solutions from other projects that provide context to possible solutions. ### Additional Context Add any other context or screenshots about the feature request here. golang-opentelemetry-otel-1.1.0/.github/codecov.yaml000066400000000000000000000005571414226744000224740ustar00rootroot00000000000000codecov: require_ci_to_pass: yes ignore: - "exporters/jaeger/internal/gen-go/**/*" - "exporters/jaeger/internal/third_party/**/*" coverage: precision: 1 round: down range: "70...100" status: project: default: target: auto threshold: 0.5% comment: layout: "reach,diff,flags,tree" behavior: default require_changes: yes golang-opentelemetry-otel-1.1.0/.github/dependabot.yml000066400000000000000000000154771414226744000230250ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates # \todo Eliminate duplication when/if Dependabot supports YAML anchors version: 2 updates: - package-ecosystem: github-actions directory: / labels: - dependencies - actions - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: / labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /bridge/opentracing labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /bridge/opencensus labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /bridge/opencensus/test labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/fib labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/prom-collector labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/jaeger labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/namedtracer labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/opencensus labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/otel-collector labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/passthrough labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/prometheus labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /example/zipkin labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/prometheus labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/stdout/stdouttrace labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/stdout/stdoutmetric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/jaeger labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/zipkin labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /sdk labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /internal/metric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /internal/tools labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /metric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /sdk/export/metric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /sdk/metric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /internal/tools/semconv-gen labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlptrace labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlptrace/otlptracegrpc labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlptrace/otlptracehttp labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlpmetric labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlpmetric/otlpmetricgrpc labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /trace labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /exporters/otlp/otlpmetric/otlpmetrichttp labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly - package-ecosystem: gomod directory: /schema labels: - dependencies - go - "Skip Changelog" schedule: day: sunday interval: weekly golang-opentelemetry-otel-1.1.0/.github/workflows/000077500000000000000000000000001414226744000222145ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/.github/workflows/changelog.yml000066400000000000000000000022051414226744000246650ustar00rootroot00000000000000# This action requires that any PR targeting the main branch should touch at # least one CHANGELOG file. If a CHANGELOG entry is not required, add the "Skip # Changelog" label to disable this action. name: changelog on: pull_request: types: [opened, synchronize, reopened, labeled, unlabeled] branches: - main jobs: changelog: runs-on: ubuntu-latest if: "!contains(github.event.pull_request.labels.*.name, 'Skip Changelog')" steps: - uses: actions/checkout@v2 - name: Check for CHANGELOG changes run: | # Only the latest commit of the feature branch is available # automatically. To diff with the base branch, we need to # fetch that too (and we only need its latest commit). git fetch origin ${{ github.base_ref }} --depth=1 if [[ $(git diff --name-only FETCH_HEAD | grep CHANGELOG) ]] then echo "A CHANGELOG was modified. Looks good!" else echo "No CHANGELOG was modified." echo "Please add a CHANGELOG entry, or add the \"Skip Changelog\" label if not required." false fi golang-opentelemetry-otel-1.1.0/.github/workflows/ci.yml000066400000000000000000000110151414226744000233300ustar00rootroot00000000000000name: ci on: push: branches: - main pull_request: env: # Path to where test results will be saved. TEST_RESULTS: /tmp/test-results # Default minimum version of Go to support. DEFAULT_GO_VERSION: 1.15 jobs: lint: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo uses: actions/checkout@v2 - name: Setup Environment run: | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "$(go env GOPATH)/bin" >> $GITHUB_PATH - name: Module cache uses: actions/cache@v2.1.6 env: cache-name: go-mod-cache with: path: ~/go/pkg/mod key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }} - name: Tools cache uses: actions/cache@v2.1.6 env: cache-name: go-tools-cache with: path: ~/.tools key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('./internal/tools/**') }} - name: Run linters run: make dependabot-check license-check lint vanity-import-check - name: Build run: make examples build - name: Check clean repository run: make check-clean-work-tree test-race: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo uses: actions/checkout@v2 - name: Setup Environment run: | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "$(go env GOPATH)/bin" >> $GITHUB_PATH - name: Module cache uses: actions/cache@v2.1.6 env: cache-name: go-mod-cache with: path: ~/go/pkg/mod key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }} - name: Run tests with race detector run: make test-race test-coverage: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo uses: actions/checkout@v2 - name: Setup Environment run: | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "$(go env GOPATH)/bin" >> $GITHUB_PATH - name: Module cache uses: actions/cache@v2.1.6 env: cache-name: go-mod-cache with: path: ~/go/pkg/mod key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }} - name: Run coverage tests run: | make test-coverage mkdir $TEST_RESULTS cp coverage.out $TEST_RESULTS cp coverage.txt $TEST_RESULTS cp coverage.html $TEST_RESULTS - name: Upload coverage report uses: codecov/codecov-action@v2.1.0 with: file: ./coverage.txt fail_ci_if_error: true verbose: true - name: Store coverage test output uses: actions/upload-artifact@v2 with: name: opentelemetry-go-test-output path: ${{ env.TEST_RESULTS }} compatibility-test: strategy: matrix: go-version: [1.17, 1.16, 1.15] os: [ubuntu-latest, macos-latest, windows-latest] # GitHub Actions does not support arm* architectures on default # runners. It is possible to acomplish this with a self-hosted runner # if we want to add this in the future: # https://docs.github.com/en/actions/hosting-your-own-runners/using-self-hosted-runners-in-a-workflow arch: ["386", amd64] exclude: # Not a supported Go OS/architecture. - os: macos-latest arch: "386" runs-on: ${{ matrix.os }} steps: - name: Install Go uses: actions/setup-go@v2.1.4 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - name: Setup Environment run: | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "$(go env GOPATH)/bin" >> $GITHUB_PATH shell: bash - name: Module cache uses: actions/cache@v2.1.6 env: cache-name: go-mod-cache with: path: ~/go/pkg/mod key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }} - name: Run tests env: GOARCH: ${{ matrix.arch }} run: make test-short golang-opentelemetry-otel-1.1.0/.github/workflows/codeql-analysis.yml000066400000000000000000000021241414226744000260260ustar00rootroot00000000000000name: "CodeQL Analysis" on: workflow_dispatch: schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) # │ │ │ │ │ # │ │ │ │ │ # │ │ │ │ │ # * * * * * - cron: '30 1 * * *' jobs: CodeQL-Build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: go - name: Autobuild uses: github/codeql-action/autobuild@v1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 golang-opentelemetry-otel-1.1.0/.github/workflows/dependabot.yml000066400000000000000000000013771414226744000250540ustar00rootroot00000000000000name: Dependabot-Tidier on: pull_request: types: [ labeled ] jobs: mod_tidier: if: ${{ contains(github.event.pull_request.labels.*.name, 'dependencies') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: ref: ${{ github.head_ref }} - uses: actions/setup-go@v2.1.4 with: go-version: '^1.15.0' - uses: evantorrie/mott-the-tidier@v1-beta id: modtidy with: gomods: '**/go.mod' gomodsum_only: true - uses: stefanzweifel/git-auto-commit-action@v4 id: autocommit with: commit_message: Auto-fix go.sum changes in dependent modules - name: changes run: | echo "Changes detected: ${{ steps.autocommit.outputs.changes_detected }}" golang-opentelemetry-otel-1.1.0/.github/workflows/gosec.yml000066400000000000000000000016131414226744000240400ustar00rootroot00000000000000name: Run Gosec on: workflow_dispatch: schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) # │ │ ┌───────────── day of the month (1 - 31) # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) # │ │ │ │ │ # │ │ │ │ │ # │ │ │ │ │ # * * * * * - cron: '30 2 * * *' jobs: tests: runs-on: ubuntu-latest env: GO111MODULE: on steps: - name: Checkout Source uses: actions/checkout@v2 - name: Run Gosec Security Scanner uses: securego/gosec@master with: args: ./... golang-opentelemetry-otel-1.1.0/.github/workflows/markdown.yml000066400000000000000000000025061414226744000245640ustar00rootroot00000000000000name: markdown on: push: branches: - main pull_request: jobs: changedfiles: name: changed files runs-on: ubuntu-latest outputs: md: ${{ steps.changes.outputs.md }} steps: - name: Checkout Repo uses: actions/checkout@v2 with: fetch-depth: 0 - name: Get changed files id: changes run: | echo "::set-output name=md::$(git diff --name-only --diff-filter=ACMRTUXB origin/${{ github.event.pull_request.base.ref }} ${{ github.event.pull_request.head.sha }} | grep .md$ | xargs)" lint: name: lint markdown files runs-on: ubuntu-latest needs: changedfiles if: ${{needs.changedfiles.outputs.md}} steps: - name: Checkout Repo uses: actions/checkout@v2 - name: Run linter uses: docker://avtodev/markdown-lint:v1 with: args: ${{needs.changedfiles.outputs.md}} check-links: runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v2 with: fetch-depth: 0 - uses: gaurav-nelson/github-action-markdown-link-check@v1 with: base-branch: 'main' use-quiet-mode: 'yes' use-verbose-mode: 'yes' config-file: '.markdown-link.json' check-modified-files-only: 'yes' folder-path: '' golang-opentelemetry-otel-1.1.0/.gitignore000066400000000000000000000005271414226744000206130ustar00rootroot00000000000000.DS_Store Thumbs.db .tools/ .idea/ .vscode/ *.iml *.so coverage.* gen/ /example/fib/fib /example/jaeger/jaeger /example/namedtracer/namedtracer /example/opencensus/opencensus /example/passthrough/passthrough /example/prometheus/prometheus /example/prom-collector/prom-collector /example/zipkin/zipkin /example/otel-collector/otel-collector golang-opentelemetry-otel-1.1.0/.gitmodules000066400000000000000000000002231414226744000207710ustar00rootroot00000000000000[submodule "opentelemetry-proto"] path = exporters/otlp/internal/opentelemetry-proto url = https://github.com/open-telemetry/opentelemetry-proto golang-opentelemetry-otel-1.1.0/.golangci.yml000066400000000000000000000013561414226744000212100ustar00rootroot00000000000000# See https://github.com/golangci/golangci-lint#config-file run: issues-exit-code: 1 #Default tests: true #Default linters: enable: - misspell - goimports - revive - gofmt issues: exclude-rules: # helpers in tests often (rightfully) pass a *testing.T as their first argument - path: _test\.go text: "context.Context should be the first parameter of a function" linters: - revive # Yes, they are, but it's okay in a test - path: _test\.go text: "exported func.*returns unexported type.*which can be annoying to use" linters: - revive linters-settings: misspell: locale: US ignore-words: - cancelled goimports: local-prefixes: go.opentelemetry.io golang-opentelemetry-otel-1.1.0/.markdown-link.json000066400000000000000000000005171414226744000223500ustar00rootroot00000000000000{ "ignorePatterns": [ { "pattern": "^http(s)?://localhost" } ], "replacementPatterns": [ { "pattern": "^/registry", "replacement": "https://opentelemetry.io/registry" } ], "retryOn429": true, "retryCount": 5, "fallbackRetryDelay": "30s" } golang-opentelemetry-otel-1.1.0/.markdownlint.yaml000066400000000000000000000004651414226744000222770ustar00rootroot00000000000000# Default state for all rules default: true # ul-style MD004: false # hard-tabs MD010: false # line-length MD013: false # no-duplicate-header MD024: siblings_only: true #single-title MD025: false # ol-prefix MD029: style: ordered # no-inline-html MD033: false # fenced-code-language MD040: false golang-opentelemetry-otel-1.1.0/CHANGELOG.md000066400000000000000000003366421414226744000204460ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## Changed - Metric SDK `export.ExportKind`, `export.ExportKindSelector` types have been renamed to `aggregation.Temporality` and `aggregation.TemporalitySelector` respectively to keep in line with current specification and protocol along with built-in selectors (e.g., `aggregation.CumulativeTemporalitySelector`, ...). (#2274) - The Metric `Exporter` interface now requires a `TemporalitySelector` method instead of an `ExportKindSelector`. (#2274) - Metrics API cleanup. The `metric/sdkapi` package has been created to relocate the API-to-SDK interface: - The following interface types simply moved from `metric` to `metric/sdkapi`: `Descriptor`, `MeterImpl`, `InstrumentImpl`, `SyncImpl`, `BoundSyncImpl`, `AsyncImpl`, `AsyncRunner`, `AsyncSingleRunner`, and `AsyncBatchRunner` - The following struct types moved and are replaced with type aliases, since they are exposed to the user: `Observation`, `Measurement`. - The No-op implementations of sync and async instruments are no longer exported, new functions `sdkapi.NewNoopAsyncInstrument()` and `sdkapi.NewNoopSyncInstrument()` are provided instead. (#2271) ### Added - Add the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) - Added a new `schema` module to help parse Schema Files in OTEP 0152 format. (#2267) ## [1.1.0] - 2021-10-27 ### Added - Add the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) - Add the `go.opentelemetry.io/otel/semconv/v1.7.0` package. The package contains semantic conventions from the `v1.7.0` version of the OpenTelemetry specification. (#2320) - Add the `go.opentelemetry.io/otel/semconv/v1.6.1` package. The package contains semantic conventions from the `v1.6.1` version of the OpenTelemetry specification. (#2321) - Add the `go.opentelemetry.io/otel/semconv/v1.5.0` package. The package contains semantic conventions from the `v1.5.0` version of the OpenTelemetry specification. (#2322) - When upgrading from the `semconv/v1.4.0` package note the following name changes: - `K8SReplicasetUIDKey` -> `K8SReplicaSetUIDKey` - `K8SReplicasetNameKey` -> `K8SReplicaSetNameKey` - `K8SStatefulsetUIDKey` -> `K8SStatefulSetUIDKey` - `k8SStatefulsetNameKey` -> `K8SStatefulSetNameKey` - `K8SDaemonsetUIDKey` -> `K8SDaemonSetUIDKey` - `K8SDaemonsetNameKey` -> `K8SDaemonSetNameKey` ### Changed - Links added to a span will be dropped by the SDK if they contain an invalid span context (#2275). ### Fixed - The `"go.opentelemetry.io/otel/semconv/v1.4.0".HTTPServerAttributesFromHTTPRequest` now correctly only sets the HTTP client IP attribute even if the connection was routed with proxies and there are multiple addresses in the `X-Forwarded-For` header. (#2282, #2284) - The `"go.opentelemetry.io/otel/semconv/v1.4.0".NetAttributesFromHTTPRequest` function correctly handles IPv6 addresses as IP addresses and sets the correct net peer IP instead of the net peer hostname attribute. (#2283, #2285) - The simple span processor shutdown method deterministically returns the exporter error status if it simultaneously finishes when the deadline is reached. (#2290, #2289) ## [1.0.1] - 2021-10-01 ### Fixed - json stdout exporter no longer crashes due to concurrency bug. (#2265) ## [Metrics 0.24.0] - 2021-10-01 ### Changed - NoopMeterProvider is now private and NewNoopMeterProvider must be used to obtain a noopMeterProvider. (#2237) - The Metric SDK `Export()` function takes a new two-level reader interface for iterating over results one instrumentation library at a time. (#2197) - The former `"go.opentelemetry.io/otel/sdk/export/metric".CheckpointSet` is renamed `Reader`. - The new interface is named `"go.opentelemetry.io/otel/sdk/export/metric".InstrumentationLibraryReader`. ## [1.0.0] - 2021-09-20 This is the first stable release for the project. This release includes an API and SDK for the tracing signal that will comply with the stability guarantees defined by the projects [versioning policy](./VERSIONING.md). ### Added - OTLP trace exporter now sets the `SchemaURL` field in the exported telemetry if the Tracer has `WithSchemaURL` option. (#2242) ### Fixed - Slice-valued attributes can correctly be used as map keys. (#2223) ### Removed - Removed the `"go.opentelemetry.io/otel/exporters/zipkin".WithSDKOptions` function. (#2248) - Removed the deprecated package `go.opentelemetry.io/otel/oteltest`. (#2234) - Removed the deprecated package `go.opentelemetry.io/otel/bridge/opencensus/utils`. (#2233) - Removed deprecated functions, types, and methods from `go.opentelemetry.io/otel/attribute` package. Use the typed functions and methods added to the package instead. (#2235) - The `Key.Array` method is removed. - The `Array` function is removed. - The `Any` function is removed. - The `ArrayValue` function is removed. - The `AsArray` function is removed. ## [1.0.0-RC3] - 2021-09-02 ### Added - Added `ErrorHandlerFunc` to use a function as an `"go.opentelemetry.io/otel".ErrorHandler`. (#2149) - Added `"go.opentelemetry.io/otel/trace".WithStackTrace` option to add a stack trace when using `span.RecordError` or when panic is handled in `span.End`. (#2163) - Added typed slice attribute types and functionality to the `go.opentelemetry.io/otel/attribute` package to replace the existing array type and functions. (#2162) - `BoolSlice`, `IntSlice`, `Int64Slice`, `Float64Slice`, and `StringSlice` replace the use of the `Array` function in the package. - Added the `go.opentelemetry.io/otel/example/fib` example package. Included is an example application that computes Fibonacci numbers. (#2203) ### Changed - Metric instruments have been renamed to match the (feature-frozen) metric API specification: - ValueRecorder becomes Histogram - ValueObserver becomes Gauge - SumObserver becomes CounterObserver - UpDownSumObserver becomes UpDownCounterObserver The API exported from this project is still considered experimental. (#2202) - Metric SDK/API implementation type `InstrumentKind` moves into `sdkapi` sub-package. (#2091) - The Metrics SDK export record no longer contains a Resource pointer, the SDK `"go.opentelemetry.io/otel/sdk/trace/export/metric".Exporter.Export()` function for push-based exporters now takes a single Resource argument, pull-based exporters use `"go.opentelemetry.io/otel/sdk/metric/controller/basic".Controller.Resource()`. (#2120) - The JSON output of the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` is harmonized now such that the output is "plain" JSON objects after each other of the form `{ ... } { ... } { ... }`. Earlier the JSON objects describing a span were wrapped in a slice for each `Exporter.ExportSpans` call, like `[ { ... } ][ { ... } { ... } ]`. Outputting JSON object directly after each other is consistent with JSON loggers, and a bit easier to parse and read. (#2196) - Update the `NewTracerConfig`, `NewSpanStartConfig`, `NewSpanEndConfig`, and `NewEventConfig` function in the `go.opentelemetry.io/otel/trace` package to return their respective configurations as structs instead of pointers to the struct. (#2212) ### Deprecated - The `go.opentelemetry.io/otel/bridge/opencensus/utils` package is deprecated. All functionality from this package now exists in the `go.opentelemetry.io/otel/bridge/opencensus` package. The functions from that package should be used instead. (#2166) - The `"go.opentelemetry.io/otel/attribute".Array` function and the related `ARRAY` value type is deprecated. Use the typed `*Slice` functions and types added to the package instead. (#2162) - The `"go.opentelemetry.io/otel/attribute".Any` function is deprecated. Use the typed functions instead. (#2181) - The `go.opentelemetry.io/otel/oteltest` package is deprecated. The `"go.opentelemetry.io/otel/sdk/trace/tracetest".SpanRecorder` can be registered with the default SDK (`go.opentelemetry.io/otel/sdk/trace`) as a `SpanProcessor` and used as a replacement for this deprecated package. (#2188) ### Removed - Removed metrics test package `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#2105) ### Fixed - The `fromEnv` detector no longer throws an error when `OTEL_RESOURCE_ATTRIBUTES` environment variable is not set or empty. (#2138) - Setting the global `ErrorHandler` with `"go.opentelemetry.io/otel".SetErrorHandler` multiple times is now supported. (#2160, #2140) - The `"go.opentelemetry.io/otel/attribute".Any` function now supports `int32` values. (#2169) - Multiple calls to `"go.opentelemetry.io/otel/sdk/metric/controller/basic".WithResource()` are handled correctly, and when no resources are provided `"go.opentelemetry.io/otel/sdk/resource".Default()` is used. (#2120) - The `WithoutTimestamps` option for the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter causes the exporter to correctly ommit timestamps. (#2195) - Fixed typos in resources.go. (#2201) ## [1.0.0-RC2] - 2021-07-26 ### Added - Added `WithOSDescription` resource configuration option to set OS (Operating System) description resource attribute (`os.description`). (#1840) - Added `WithOS` resource configuration option to set all OS (Operating System) resource attributes at once. (#1840) - Added the `WithRetry` option to the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. This option is a replacement for the removed `WithMaxAttempts` and `WithBackoff` options. (#2095) - Added API `LinkFromContext` to return Link which encapsulates SpanContext from provided context and also encapsulates attributes. (#2115) - Added a new `Link` type under the SDK `otel/sdk/trace` package that counts the number of attributes that were dropped for surpassing the `AttributePerLinkCountLimit` configured in the Span's `SpanLimits`. This new type replaces the equal-named API `Link` type found in the `otel/trace` package for most usages within the SDK. For example, instances of this type are now returned by the `Links()` function of `ReadOnlySpan`s provided in places like the `OnEnd` function of `SpanProcessor` implementations. (#2118) - Added the `SpanRecorder` type to the `go.opentelemetry.io/otel/skd/trace/tracetest` package. This type can be used with the default SDK as a `SpanProcessor` during testing. (#2132) ### Changed - The `SpanModels` function is now exported from the `go.opentelemetry.io/otel/exporters/zipkin` package to convert OpenTelemetry spans into Zipkin model spans. (#2027) - Rename the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".RetrySettings` to `RetryConfig`. (#2095) ### Deprecated - The `TextMapCarrier` and `TextMapPropagator` from the `go.opentelemetry.io/otel/oteltest` package and their associated creation functions (`TextMapCarrier`, `NewTextMapPropagator`) are deprecated. (#2114) - The `Harness` type from the `go.opentelemetry.io/otel/oteltest` package and its associated creation function, `NewHarness` are deprecated and will be removed in the next release. (#2123) - The `TraceStateFromKeyValues` function from the `go.opentelemetry.io/otel/oteltest` package is deprecated. Use the `trace.ParseTraceState` function instead. (#2122) ### Removed - Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/jaeger`. (#2020) - Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/zipkin`. (#2020) - Removed the `"go.opentelemetry.io/otel/sdk/resource".WithBuiltinDetectors` function. The explicit `With*` options for every built-in detector should be used instead. (#2026 #2097) - Removed the `WithMaxAttempts` and `WithBackoff` options from the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. The retry logic of the package has been updated to match the `otlptracegrpc` package and accordingly a `WithRetry` option is added that should be used instead. (#2095) - Removed `DroppedAttributeCount` field from `otel/trace.Link` struct. (#2118) ### Fixed - When using WithNewRoot, don't use the parent context for making sampling decisions. (#2032) - `oteltest.Tracer` now creates a valid `SpanContext` when using `WithNewRoot`. (#2073) - OS type detector now sets the correct `dragonflybsd` value for DragonFly BSD. (#2092) - The OTel span status is correctly transformed into the OTLP status in the `go.opentelemetry.io/otel/exporters/otlp/otlptrace` package. This fix will by default set the status to `Unset` if it is not explicitly set to `Ok` or `Error`. (#2099 #2102) - The `Inject` method for the `"go.opentelemetry.io/otel/propagation".TraceContext` type no longer injects empty `tracestate` values. (#2108) - Use `6831` as default Jaeger agent port instead of `6832`. (#2131) ## [Experimental Metrics v0.22.0] - 2021-07-19 ### Added - Adds HTTP support for OTLP metrics exporter. (#2022) ### Removed - Removed the deprecated package `go.opentelemetry.io/otel/exporters/metric/prometheus`. (#2020) ## [1.0.0-RC1] / 0.21.0 - 2021-06-18 With this release we are introducing a split in module versions. The tracing API and SDK are entering the `v1.0.0` Release Candidate phase with `v1.0.0-RC1` while the experimental metrics API and SDK continue with `v0.x` releases at `v0.21.0`. Modules at major version 1 or greater will not depend on modules with major version 0. ### Added - Adds `otlpgrpc.WithRetry`option for configuring the retry policy for transient errors on the otlp/gRPC exporter. (#1832) - The following status codes are defined as transient errors: | gRPC Status Code | Description | | ---------------- | ----------- | | 1 | Cancelled | | 4 | Deadline Exceeded | | 8 | Resource Exhausted | | 10 | Aborted | | 10 | Out of Range | | 14 | Unavailable | | 15 | Data Loss | - Added `Status` type to the `go.opentelemetry.io/otel/sdk/trace` package to represent the status of a span. (#1874) - Added `SpanStub` type and its associated functions to the `go.opentelemetry.io/otel/sdk/trace/tracetest` package. This type can be used as a testing replacement for the `SpanSnapshot` that was removed from the `go.opentelemetry.io/otel/sdk/trace` package. (#1873) - Adds support for scheme in `OTEL_EXPORTER_OTLP_ENDPOINT` according to the spec. (#1886) - Adds `trace.WithSchemaURL` option for configuring the tracer with a Schema URL. (#1889) - Added an example of using OpenTelemetry Go as a trace context forwarder. (#1912) - `ParseTraceState` is added to the `go.opentelemetry.io/otel/trace` package. It can be used to decode a `TraceState` from a `tracestate` header string value. (#1937) - Added `Len` method to the `TraceState` type in the `go.opentelemetry.io/otel/trace` package. This method returns the number of list-members the `TraceState` holds. (#1937) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace` that defines a trace exporter that uses a `otlptrace.Client` to send data. Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` implementing a gRPC `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing .(#1922) - Added `Baggage`, `Member`, and `Property` types to the `go.opentelemetry.io/otel/baggage` package along with their related functions. (#1967) - Added `ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext` functions to the `go.opentelemetry.io/otel/baggage` package. These functions replace the `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions from that package and directly work with the new `Baggage` type. (#1967) - The `OTEL_SERVICE_NAME` environment variable is the preferred source for `service.name`, used by the environment resource detector if a service name is present both there and in `OTEL_RESOURCE_ATTRIBUTES`. (#1969) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` implementing an HTTP `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing. (#1963) - Changes `go.opentelemetry.io/otel/sdk/resource.NewWithAttributes` to require a schema URL. The old function is still available as `resource.NewSchemaless`. This is a breaking change. (#1938) - Several builtin resource detectors now correctly populate the schema URL. (#1938) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` that defines a metrics exporter that uses a `otlpmetric.Client` to send data. - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` implementing a gRPC `otlpmetric.Client` and offers convenience functions, `New` and `NewUnstarted`, to create an `otlpmetric.Exporter`.(#1991) - Added `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter. (#2005) - Added `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` exporter. (#2005) - Added a `TracerProvider()` method to the `"go.opentelemetry.io/otel/trace".Span` interface. This can be used to obtain a `TracerProvider` from a given span that utilizes the same trace processing pipeline. (#2009) ### Changed - Make `NewSplitDriver` from `go.opentelemetry.io/otel/exporters/otlp` take variadic arguments instead of a `SplitConfig` item. `NewSplitDriver` now automatically implements an internal `noopDriver` for `SplitConfig` fields that are not initialized. (#1798) - `resource.New()` now creates a Resource without builtin detectors. Previous behavior is now achieved by using `WithBuiltinDetectors` Option. (#1810) - Move the `Event` type from the `go.opentelemetry.io/otel` package to the `go.opentelemetry.io/otel/sdk/trace` package. (#1846) - CI builds validate against last two versions of Go, dropping 1.14 and adding 1.16. (#1865) - BatchSpanProcessor now report export failures when calling `ForceFlush()` method. (#1860) - `Set.Encoded(Encoder)` no longer caches the result of an encoding. (#1855) - Renamed `CloudZoneKey` to `CloudAvailabilityZoneKey` in Resource semantic conventions according to spec. (#1871) - The `StatusCode` and `StatusMessage` methods of the `ReadOnlySpan` interface and the `Span` produced by the `go.opentelemetry.io/otel/sdk/trace` package have been replaced with a single `Status` method. This method returns the status of a span using the new `Status` type. (#1874) - Updated `ExportSpans` method of the`SpanExporter` interface type to accept `ReadOnlySpan`s instead of the removed `SpanSnapshot`. This brings the export interface into compliance with the specification in that it now accepts an explicitly immutable type instead of just an implied one. (#1873) - Unembed `SpanContext` in `Link`. (#1877) - Generate Semantic conventions from the specification YAML. (#1891) - Spans created by the global `Tracer` obtained from `go.opentelemetry.io/otel`, prior to a functioning `TracerProvider` being set, now propagate the span context from their parent if one exists. (#1901) - The `"go.opentelemetry.io/otel".Tracer` function now accepts tracer options. (#1902) - Move the `go.opentelemetry.io/otel/unit` package to `go.opentelemetry.io/otel/metric/unit`. (#1903) - Changed `go.opentelemetry.io/otel/trace.TracerConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config.) (#1921) - Changed `go.opentelemetry.io/otel/trace.SpanConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Changed `span.End()` now only accepts Options that are allowed at `End()`. (#1921) - Changed `go.opentelemetry.io/otel/metric.InstrumentConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Changed `go.opentelemetry.io/otel/metric.MeterConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Refactored option types according to the contribution style guide. (#1882) - Move the `go.opentelemetry.io/otel/trace.TraceStateFromKeyValues` function to the `go.opentelemetry.io/otel/oteltest` package. This function is preserved for testing purposes where it may be useful to create a `TraceState` from `attribute.KeyValue`s, but it is not intended for production use. The new `ParseTraceState` function should be used to create a `TraceState`. (#1931) - Updated `MarshalJSON` method of the `go.opentelemetry.io/otel/trace.TraceState` type to marshal the type into the string representation of the `TraceState`. (#1931) - The `TraceState.Delete` method from the `go.opentelemetry.io/otel/trace` package no longer returns an error in addition to a `TraceState`. (#1931) - Updated `Get` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) - Updated `Insert` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a pair of `string`s instead of an `attribute.KeyValue` type. (#1931) - Updated `Delete` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/stdout` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/metric/prometheus` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) - Renamed `NewUnstartedExporter` to `NewUnstarted` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) - The `go.opentelemetry.io/otel/semconv` package has been moved to `go.opentelemetry.io/otel/semconv/v1.4.0` to allow for multiple [telemetry schema](https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md) versions to be used concurrently. (#1987) - Metrics test helpers in `go.opentelemetry.io/otel/oteltest` have been moved to `go.opentelemetry.io/otel/metric/metrictest`. (#1988) ### Deprecated - The `go.opentelemetry.io/otel/exporters/metric/prometheus` is deprecated, use `go.opentelemetry.io/otel/exporters/prometheus` instead. (#1993) - The `go.opentelemetry.io/otel/exporters/trace/jaeger` is deprecated, use `go.opentelemetry.io/otel/exporters/jaeger` instead. (#1993) - The `go.opentelemetry.io/otel/exporters/trace/zipkin` is deprecated, use `go.opentelemetry.io/otel/exporters/zipkin` instead. (#1993) ### Removed - Removed `resource.WithoutBuiltin()`. Use `resource.New()`. (#1810) - Unexported types `resource.FromEnv`, `resource.Host`, and `resource.TelemetrySDK`, Use the corresponding `With*()` to use individually. (#1810) - Removed the `Tracer` and `IsRecording` method from the `ReadOnlySpan` in the `go.opentelemetry.io/otel/sdk/trace`. The `Tracer` method is not a required to be included in this interface and given the mutable nature of the tracer that is associated with a span, this method is not appropriate. The `IsRecording` method returns if the span is recording or not. A read-only span value does not need to know if updates to it will be recorded or not. By definition, it cannot be updated so there is no point in communicating if an update is recorded. (#1873) - Removed the `SpanSnapshot` type from the `go.opentelemetry.io/otel/sdk/trace` package. The use of this type has been replaced with the use of the explicitly immutable `ReadOnlySpan` type. When a concrete representation of a read-only span is needed for testing, the newly added `SpanStub` in the `go.opentelemetry.io/otel/sdk/trace/tracetest` package should be used. (#1873) - Removed the `Tracer` method from the `Span` interface in the `go.opentelemetry.io/otel/trace` package. Using the same tracer that created a span introduces the error where an instrumentation library's `Tracer` is used by other code instead of their own. The `"go.opentelemetry.io/otel".Tracer` function or a `TracerProvider` should be used to acquire a library specific `Tracer` instead. (#1900) - The `TracerProvider()` method on the `Span` interface may also be used to obtain a `TracerProvider` using the same trace processing pipeline. (#2009) - The `http.url` attribute generated by `HTTPClientAttributesFromHTTPRequest` will no longer include username or password information. (#1919) - Removed `IsEmpty` method of the `TraceState` type in the `go.opentelemetry.io/otel/trace` package in favor of using the added `TraceState.Len` method. (#1931) - Removed `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions in the `go.opentelemetry.io/otel/baggage` package. Handling of baggage is now done using the added `Baggage` type and related context functions (`ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext`) in that package. (#1967) - The `InstallNewPipeline` and `NewExportPipeline` creation functions in all the exporters (prometheus, otlp, stdout, jaeger, and zipkin) have been removed. These functions were deemed premature attempts to provide convenience that did not achieve this aim. (#1985) - The `go.opentelemetry.io/otel/exporters/otlp` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/otlp/otlptrace` instead. (#1990) - The `go.opentelemetry.io/otel/exporters/stdout` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` or `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` instead. (#2005) ### Fixed - Only report errors from the `"go.opentelemetry.io/otel/sdk/resource".Environment` function when they are not `nil`. (#1850, #1851) - The `Shutdown` method of the simple `SpanProcessor` in the `go.opentelemetry.io/otel/sdk/trace` package now honors the context deadline or cancellation. (#1616, #1856) - BatchSpanProcessor now drops span batches that failed to be exported. (#1860) - Use `http://localhost:14268/api/traces` as default Jaeger collector endpoint instead of `http://localhost:14250`. (#1898) - Allow trailing and leading whitespace in the parsing of a `tracestate` header. (#1931) - Add logic to determine if the channel is closed to fix Jaeger exporter test panic with close closed channel. (#1870, #1973) - Avoid transport security when OTLP endpoint is a Unix socket. (#2001) ### Security ## [0.20.0] - 2021-04-23 ### Added - The OTLP exporter now has two new convenience functions, `NewExportPipeline` and `InstallNewPipeline`, setup and install the exporter in tracing and metrics pipelines. (#1373) - Adds semantic conventions for exceptions. (#1492) - Added Jaeger Environment variables: `OTEL_EXPORTER_JAEGER_AGENT_HOST`, `OTEL_EXPORTER_JAEGER_AGENT_PORT` These environment variables can be used to override Jaeger agent hostname and port (#1752) - Option `ExportTimeout` was added to batch span processor. (#1755) - `trace.TraceFlags` is now a defined type over `byte` and `WithSampled(bool) TraceFlags` and `IsSampled() bool` methods have been added to it. (#1770) - The `Event` and `Link` struct types from the `go.opentelemetry.io/otel` package now include a `DroppedAttributeCount` field to record the number of attributes that were not recorded due to configured limits being reached. (#1771) - The Jaeger exporter now reports dropped attributes for a Span event in the exported log. (#1771) - Adds test to check BatchSpanProcessor ignores `OnEnd` and `ForceFlush` post `Shutdown`. (#1772) - Extract resource attributes from the `OTEL_RESOURCE_ATTRIBUTES` environment variable and merge them with the `resource.Default` resource as well as resources provided to the `TracerProvider` and metric `Controller`. (#1785) - Added `WithOSType` resource configuration option to set OS (Operating System) type resource attribute (`os.type`). (#1788) - Added `WithProcess*` resource configuration options to set Process resource attributes. (#1788) - `process.pid` - `process.executable.name` - `process.executable.path` - `process.command_args` - `process.owner` - `process.runtime.name` - `process.runtime.version` - `process.runtime.description` - Adds `k8s.node.name` and `k8s.node.uid` attribute keys to the `semconv` package. (#1789) - Added support for configuring OTLP/HTTP and OTLP/gRPC Endpoints, TLS Certificates, Headers, Compression and Timeout via Environment Variables. (#1758, #1769 and #1811) - `OTEL_EXPORTER_OTLP_ENDPOINT` - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` - `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` - `OTEL_EXPORTER_OTLP_HEADERS` - `OTEL_EXPORTER_OTLP_TRACES_HEADERS` - `OTEL_EXPORTER_OTLP_METRICS_HEADERS` - `OTEL_EXPORTER_OTLP_COMPRESSION` - `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` - `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` - `OTEL_EXPORTER_OTLP_TIMEOUT` - `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` - `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` - `OTEL_EXPORTER_OTLP_CERTIFICATE` - `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` - `OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` - Adds `otlpgrpc.WithTimeout` option for configuring timeout to the otlp/gRPC exporter. (#1821) - Adds `jaeger.WithMaxPacketSize` option for configuring maximum UDP packet size used when connecting to the Jaeger agent. (#1853) ### Fixed - The `Span.IsRecording` implementation from `go.opentelemetry.io/otel/sdk/trace` always returns false when not being sampled. (#1750) - The Jaeger exporter now correctly sets tags for the Span status code and message. This means it uses the correct tag keys (`"otel.status_code"`, `"otel.status_description"`) and does not set the status message as a tag unless it is set on the span. (#1761) - The Jaeger exporter now correctly records Span event's names using the `"event"` key for a tag. Additionally, this tag is overridden, as specified in the OTel specification, if the event contains an attribute with that key. (#1768) - Zipkin Exporter: Ensure mapping between OTel and Zipkin span data complies with the specification. (#1688) - Fixed typo for default service name in Jaeger Exporter. (#1797) - Fix flaky OTLP for the reconnnection of the client connection. (#1527, #1814) - Fix Jaeger exporter dropping of span batches that exceed the UDP packet size limit. Instead, the exporter now splits the batch into smaller sendable batches. (#1828) ### Changed - Span `RecordError` now records an `exception` event to comply with the semantic convention specification. (#1492) - Jaeger exporter was updated to use thrift v0.14.1. (#1712) - Migrate from using internally built and maintained version of the OTLP to the one hosted at `go.opentelemetry.io/proto/otlp`. (#1713) - Migrate from using `github.com/gogo/protobuf` to `google.golang.org/protobuf` to match `go.opentelemetry.io/proto/otlp`. (#1713) - The storage of a local or remote Span in a `context.Context` using its SpanContext is unified to store just the current Span. The Span's SpanContext can now self-identify as being remote or not. This means that `"go.opentelemetry.io/otel/trace".ContextWithRemoteSpanContext` will now overwrite any existing current Span, not just existing remote Spans, and make it the current Span in a `context.Context`. (#1731) - Improve OTLP/gRPC exporter connection errors. (#1737) - Information about a parent span context in a `"go.opentelemetry.io/otel/export/trace".SpanSnapshot` is unified in a new `Parent` field. The existing `ParentSpanID` and `HasRemoteParent` fields are removed in favor of this. (#1748) - The `ParentContext` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is updated to hold a `context.Context` containing the parent span. This changes it to make `SamplingParameters` conform with the OpenTelemetry specification. (#1749) - Updated Jaeger Environment Variables: `JAEGER_ENDPOINT`, `JAEGER_USER`, `JAEGER_PASSWORD` to `OTEL_EXPORTER_JAEGER_ENDPOINT`, `OTEL_EXPORTER_JAEGER_USER`, `OTEL_EXPORTER_JAEGER_PASSWORD` in compliance with OTel specification. (#1752) - Modify `BatchSpanProcessor.ForceFlush` to abort after timeout/cancellation. (#1757) - The `DroppedAttributeCount` field of the `Span` in the `go.opentelemetry.io/otel` package now only represents the number of attributes dropped for the span itself. It no longer is a conglomerate of itself, events, and link attributes that have been dropped. (#1771) - Make `ExportSpans` in Jaeger Exporter honor context deadline. (#1773) - Modify Zipkin Exporter default service name, use default resource's serviceName instead of empty. (#1777) - The `go.opentelemetry.io/otel/sdk/export/trace` package is merged into the `go.opentelemetry.io/otel/sdk/trace` package. (#1778) - The prometheus.InstallNewPipeline example is moved from comment to example test (#1796) - The convenience functions for the stdout exporter have been updated to return the `TracerProvider` implementation and enable the shutdown of the exporter. (#1800) - Replace the flush function returned from the Jaeger exporter's convenience creation functions (`InstallNewPipeline` and `NewExportPipeline`) with the `TracerProvider` implementation they create. This enables the caller to shutdown and flush using the related `TracerProvider` methods. (#1822) - Updated the Jaeger exporter to have a default endpoint, `http://localhost:14250`, for the collector. (#1824) - Changed the function `WithCollectorEndpoint` in the Jaeger exporter to no longer accept an endpoint as an argument. The endpoint can be passed with the `CollectorEndpointOption` using the `WithEndpoint` function or by setting the `OTEL_EXPORTER_JAEGER_ENDPOINT` environment variable value appropriately. (#1824) - The Jaeger exporter no longer batches exported spans itself, instead it relies on the SDK's `BatchSpanProcessor` for this functionality. (#1830) - The Jaeger exporter creation functions (`NewRawExporter`, `NewExportPipeline`, and `InstallNewPipeline`) no longer accept the removed `Option` type as a variadic argument. (#1830) ### Removed - Removed Jaeger Environment variables: `JAEGER_SERVICE_NAME`, `JAEGER_DISABLED`, `JAEGER_TAGS` These environment variables will no longer be used to override values of the Jaeger exporter (#1752) - No longer set the links for a `Span` in `go.opentelemetry.io/otel/sdk/trace` that is configured to be a new root. This is unspecified behavior that the OpenTelemetry community plans to standardize in the future. To prevent backwards incompatible changes when it is specified, these links are removed. (#1726) - Setting error status while recording error with Span from oteltest package. (#1729) - The concept of a remote and local Span stored in a context is unified to just the current Span. Because of this `"go.opentelemetry.io/otel/trace".RemoteSpanContextFromContext` is removed as it is no longer needed. Instead, `"go.opentelemetry.io/otel/trace".SpanContextFromContex` can be used to return the current Span. If needed, that Span's `SpanContext.IsRemote()` can then be used to determine if it is remote or not. (#1731) - The `HasRemoteParent` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is removed. This field is redundant to the information returned from the `Remote` method of the `SpanContext` held in the `ParentContext` field. (#1749) - The `trace.FlagsDebug` and `trace.FlagsDeferred` constants have been removed and will be localized to the B3 propagator. (#1770) - Remove `Process` configuration, `WithProcessFromEnv` and `ProcessFromEnv`, and type from the Jaeger exporter package. The information that could be configured in the `Process` struct should be configured in a `Resource` instead. (#1776, #1804) - Remove the `WithDisabled` option from the Jaeger exporter. To disable the exporter unregister it from the `TracerProvider` or use a no-operation `TracerProvider`. (#1806) - Removed the functions `CollectorEndpointFromEnv` and `WithCollectorEndpointOptionFromEnv` from the Jaeger exporter. These functions for retrieving specific environment variable values are redundant of other internal functions and are not intended for end user use. (#1824) - Removed the Jaeger exporter `WithSDKOptions` `Option`. This option was used to set SDK options for the exporter creation convenience functions. These functions are provided as a way to easily setup or install the exporter with what are deemed reasonable SDK settings for common use cases. If the SDK needs to be configured differently, the `NewRawExporter` function and direct setup of the SDK with the desired settings should be used. (#1825) - The `WithBufferMaxCount` and `WithBatchMaxCount` `Option`s from the Jaeger exporter are removed. The exporter no longer batches exports, instead relying on the SDK's `BatchSpanProcessor` for this functionality. (#1830) - The Jaeger exporter `Option` type is removed. The type is no longer used by the exporter to configure anything. All the previous configurations these options provided were duplicates of SDK configuration. They have been removed in favor of using the SDK configuration and focuses the exporter configuration to be only about the endpoints it will send telemetry to. (#1830) ## [0.19.0] - 2021-03-18 ### Added - Added `Marshaler` config option to `otlphttp` to enable otlp over json or protobufs. (#1586) - A `ForceFlush` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` to flush all registered `SpanProcessor`s. (#1608) - Added `WithSampler` and `WithSpanLimits` to tracer provider. (#1633, #1702) - `"go.opentelemetry.io/otel/trace".SpanContext` now has a `remote` property, and `IsRemote()` predicate, that is true when the `SpanContext` has been extracted from remote context data. (#1701) - A `Valid` method to the `"go.opentelemetry.io/otel/attribute".KeyValue` type. (#1703) ### Changed - `trace.SpanContext` is now immutable and has no exported fields. (#1573) - `trace.NewSpanContext()` can be used in conjunction with the `trace.SpanContextConfig` struct to initialize a new `SpanContext` where all values are known. - Update the `ForceFlush` method signature to the `"go.opentelemetry.io/otel/sdk/trace".SpanProcessor` to accept a `context.Context` and return an error. (#1608) - Update the `Shutdown` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` return an error on shutdown failure. (#1608) - The SimpleSpanProcessor will now shut down the enclosed `SpanExporter` and gracefully ignore subsequent calls to `OnEnd` after `Shutdown` is called. (#1612) - `"go.opentelemetry.io/sdk/metric/controller.basic".WithPusher` is replaced with `WithExporter` to provide consistent naming across project. (#1656) - Added non-empty string check for trace `Attribute` keys. (#1659) - Add `description` to SpanStatus only when `StatusCode` is set to error. (#1662) - Jaeger exporter falls back to `resource.Default`'s `service.name` if the exported Span does not have one. (#1673) - Jaeger exporter populates Jaeger's Span Process from Resource. (#1673) - Renamed the `LabelSet` method of `"go.opentelemetry.io/otel/sdk/resource".Resource` to `Set`. (#1692) - Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1693) - Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1693) ### Removed - Removed `serviceName` parameter from Zipkin exporter and uses resource instead. (#1549) - Removed `WithConfig` from tracer provider to avoid overriding configuration. (#1633) - Removed the exported `SimpleSpanProcessor` and `BatchSpanProcessor` structs. These are now returned as a SpanProcessor interface from their respective constructors. (#1638) - Removed `WithRecord()` from `trace.SpanOption` when creating a span. (#1660) - Removed setting status to `Error` while recording an error as a span event in `RecordError`. (#1663) - Removed `jaeger.WithProcess` configuration option. (#1673) - Removed `ApplyConfig` method from `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` and the now unneeded `Config` struct. (#1693) ### Fixed - Jaeger Exporter: Ensure mapping between OTEL and Jaeger span data complies with the specification. (#1626) - `SamplingResult.TraceState` is correctly propagated to a newly created span's `SpanContext`. (#1655) - The `otel-collector` example now correctly flushes metric events prior to shutting down the exporter. (#1678) - Do not set span status message in `SpanStatusFromHTTPStatusCode` if it can be inferred from `http.status_code`. (#1681) - Synchronization issues in global trace delegate implementation. (#1686) - Reduced excess memory usage by global `TracerProvider`. (#1687) ## [0.18.0] - 2021-03-03 ### Added - Added `resource.Default()` for use with meter and tracer providers. (#1507) - `AttributePerEventCountLimit` and `AttributePerLinkCountLimit` for `SpanLimits`. (#1535) - Added `Keys()` method to `propagation.TextMapCarrier` and `propagation.HeaderCarrier` to adapt `http.Header` to this interface. (#1544) - Added `code` attributes to `go.opentelemetry.io/otel/semconv` package. (#1558) - Compatibility testing suite in the CI system for the following systems. (#1567) | OS | Go Version | Architecture | | ------- | ---------- | ------------ | | Ubuntu | 1.15 | amd64 | | Ubuntu | 1.14 | amd64 | | Ubuntu | 1.15 | 386 | | Ubuntu | 1.14 | 386 | | MacOS | 1.15 | amd64 | | MacOS | 1.14 | amd64 | | Windows | 1.15 | amd64 | | Windows | 1.14 | amd64 | | Windows | 1.15 | 386 | | Windows | 1.14 | 386 | ### Changed - Replaced interface `oteltest.SpanRecorder` with its existing implementation `StandardSpanRecorder`. (#1542) - Default span limit values to 128. (#1535) - Rename `MaxEventsPerSpan`, `MaxAttributesPerSpan` and `MaxLinksPerSpan` to `EventCountLimit`, `AttributeCountLimit` and `LinkCountLimit`, and move these fields into `SpanLimits`. (#1535) - Renamed the `otel/label` package to `otel/attribute`. (#1541) - Vendor the Jaeger exporter's dependency on Apache Thrift. (#1551) - Parallelize the CI linting and testing. (#1567) - Stagger timestamps in exact aggregator tests. (#1569) - Changed all examples to use `WithBatchTimeout(5 * time.Second)` rather than `WithBatchTimeout(5)`. (#1621) - Prevent end-users from implementing some interfaces (#1575) ``` "otel/exporters/otlp/otlphttp".Option "otel/exporters/stdout".Option "otel/oteltest".Option "otel/trace".TracerOption "otel/trace".SpanOption "otel/trace".EventOption "otel/trace".LifeCycleOption "otel/trace".InstrumentationOption "otel/sdk/resource".Option "otel/sdk/trace".ParentBasedSamplerOption "otel/sdk/trace".ReadOnlySpan "otel/sdk/trace".ReadWriteSpan ``` ### Removed - Removed attempt to resample spans upon changing the span name with `span.SetName()`. (#1545) - The `test-benchmark` is no longer a dependency of the `precommit` make target. (#1567) - Removed the `test-386` make target. This was replaced with a full compatibility testing suite (i.e. multi OS/arch) in the CI system. (#1567) ### Fixed - The sequential timing check of timestamps in the stdout exporter are now setup explicitly to be sequential (#1571). (#1572) - Windows build of Jaeger tests now compiles with OS specific functions (#1576). (#1577) - The sequential timing check of timestamps of go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue are now setup explicitly to be sequential (#1578). (#1579) - Validate tracestate header keys with vendors according to the W3C TraceContext specification (#1475). (#1581) - The OTLP exporter includes related labels for translations of a GaugeArray (#1563). (#1570) ## [0.17.0] - 2021-02-12 ### Changed - Rename project default branch from `master` to `main`. (#1505) - Reverse order in which `Resource` attributes are merged, per change in spec. (#1501) - Add tooling to maintain "replace" directives in go.mod files automatically. (#1528) - Create new modules: otel/metric, otel/trace, otel/oteltest, otel/sdk/export/metric, otel/sdk/metric (#1528) - Move metric-related public global APIs from otel to otel/metric/global. (#1528) ## Fixed - Fixed otlpgrpc reconnection issue. - The example code in the README.md of `go.opentelemetry.io/otel/exporters/otlp` is moved to a compiled example test and used the new `WithAddress` instead of `WithEndpoint`. (#1513) - The otel-collector example now uses the default OTLP receiver port of the collector. ## [0.16.0] - 2021-01-13 ### Added - Add the `ReadOnlySpan` and `ReadWriteSpan` interfaces to provide better control for accessing span data. (#1360) - `NewGRPCDriver` function returns a `ProtocolDriver` that maintains a single gRPC connection to the collector. (#1369) - Added documentation about the project's versioning policy. (#1388) - Added `NewSplitDriver` for OTLP exporter that allows sending traces and metrics to different endpoints. (#1418) - Added codeql worfklow to GitHub Actions (#1428) - Added Gosec workflow to GitHub Actions (#1429) - Add new HTTP driver for OTLP exporter in `exporters/otlp/otlphttp`. Currently it only supports the binary protobuf payloads. (#1420) - Add an OpenCensus exporter bridge. (#1444) ### Changed - Rename `internal/testing` to `internal/internaltest`. (#1449) - Rename `export.SpanData` to `export.SpanSnapshot` and use it only for exporting spans. (#1360) - Store the parent's full `SpanContext` rather than just its span ID in the `span` struct. (#1360) - Improve span duration accuracy. (#1360) - Migrated CI/CD from CircleCI to GitHub Actions (#1382) - Remove duplicate checkout from GitHub Actions workflow (#1407) - Metric `array` aggregator renamed `exact` to match its `aggregation.Kind` (#1412) - Metric `exact` aggregator includes per-point timestamps (#1412) - Metric stdout exporter uses MinMaxSumCount aggregator for ValueRecorder instruments (#1412) - `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369) - Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369) - Unify endpoint API that related to OTel exporter. (#1401) - Optimize metric histogram aggregator to re-use its slice of buckets. (#1435) - Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430) - Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434) - `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432) - Moved gRPC driver for OTLP exporter to `exporters/otlp/otlpgrpc`. (#1420) - The `TraceContext` propagator now correctly propagates `TraceState` through the `SpanContext`. (#1447) - Metric Push and Pull Controller components are combined into a single "basic" Controller: - `WithExporter()` and `Start()` to configure Push behavior - `Start()` is optional; use `Collect()` and `ForEach()` for Pull behavior - `Start()` and `Stop()` accept Context. (#1378) - The `Event` type is moved from the `otel/sdk/export/trace` package to the `otel/trace` API package. (#1452) ### Removed - Remove `errUninitializedSpan` as its only usage is now obsolete. (#1360) - Remove Metric export functionality related to quantiles and summary data points: this is not specified (#1412) - Remove DDSketch metric aggregator; our intention is to re-introduce this as an option of the histogram aggregator after [new OTLP histogram data types](https://github.com/open-telemetry/opentelemetry-proto/pull/226) are released (#1412) ### Fixed - `BatchSpanProcessor.Shutdown()` will now shutdown underlying `export.SpanExporter`. (#1443) ## [0.15.0] - 2020-12-10 ### Added - The `WithIDGenerator` `TracerProviderOption` is added to the `go.opentelemetry.io/otel/trace` package to configure an `IDGenerator` for the `TracerProvider`. (#1363) ### Changed - The Zipkin exporter now uses the Span status code to determine. (#1328) - `NewExporter` and `Start` functions in `go.opentelemetry.io/otel/exporters/otlp` now receive `context.Context` as a first parameter. (#1357) - Move the OpenCensus example into `example` directory. (#1359) - Moved the SDK's `internal.IDGenerator` interface in to the `sdk/trace` package to enable support for externally-defined ID generators. (#1363) - Bump `github.com/google/go-cmp` from 0.5.3 to 0.5.4 (#1374) - Bump `github.com/golangci/golangci-lint` in `/internal/tools` (#1375) ### Fixed - Metric SDK `SumObserver` and `UpDownSumObserver` instruments correctness fixes. (#1381) ## [0.14.0] - 2020-11-19 ### Added - An `EventOption` and the related `NewEventConfig` function are added to the `go.opentelemetry.io/otel` package to configure Span events. (#1254) - A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test `TextMap` type propagators and their use. (#1259) - `SpanContextFromContext` returns `SpanContext` from context. (#1255) - `TraceState` has been added to `SpanContext`. (#1340) - `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323) - Add an OpenCensus to OpenTelemetry tracing bridge. (#1305) - Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333) - Add missing tests for `sdk/trace/attributes_map.go`. (#1337) ### Changed - Move the `go.opentelemetry.io/otel/api/trace` package into `go.opentelemetry.io/otel/trace` with the following changes. (#1229) (#1307) - `ID` has been renamed to `TraceID`. - `IDFromHex` has been renamed to `TraceIDFromHex`. - `EmptySpanContext` is removed. - Move the `go.opentelemetry.io/otel/api/trace/tracetest` package into `go.opentelemetry.io/otel/oteltest`. (#1229) - OTLP Exporter updates: - supports OTLP v0.6.0 (#1230, #1354) - supports configurable aggregation temporality (default: Cumulative, optional: Stateless). (#1296) - The Sampler is now called on local child spans. (#1233) - The `Kind` type from the `go.opentelemetry.io/otel/api/metric` package was renamed to `InstrumentKind` to more specifically describe what it is and avoid semantic ambiguity. (#1240) - The `MetricKind` method of the `Descriptor` type in the `go.opentelemetry.io/otel/api/metric` package was renamed to `Descriptor.InstrumentKind`. This matches the returned type and fixes misuse of the term metric. (#1240) - Move test harness from the `go.opentelemetry.io/otel/api/apitest` package into `go.opentelemetry.io/otel/oteltest`. (#1241) - Move the `go.opentelemetry.io/otel/api/metric/metrictest` package into `go.opentelemetry.io/oteltest` as part of #964. (#1252) - Move the `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric` as part of #1303. (#1321) - Move the `go.opentelemetry.io/otel/api/metric/registry` package into `go.opentelemetry.io/otel/metric/registry` as a part of #1303. (#1316) - Move the `Number` type (together with related functions) from `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric/number` as a part of #1303. (#1316) - The function signature of the Span `AddEvent` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required name and a variable number of `EventOption`s. (#1254) - The function signature of the Span `RecordError` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required error value and a variable number of `EventOption`s. (#1254) - Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel`. (#1262) (#1330) - Move the `Version` function from `go.opentelemetry.io/otel/sdk` to `go.opentelemetry.io/otel`. (#1330) - Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267) - Fix `Code.UnmarshalJSON` to work with valid JSON only. (#1276) - The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235) - The Prometheus exporter now exports non-monotonic counters (i.e. `UpDownCounter`s) as gauges. (#1210) - Correct the `Span.End` method documentation in the `otel` API to state updates are not allowed on a span after it has ended. (#1310) - Updated span collection limits for attribute, event and link counts to 1000 (#1318) - Renamed `semconv.HTTPUrlKey` to `semconv.HTTPURLKey`. (#1338) ### Removed - The `ErrInvalidHexID`, `ErrInvalidTraceIDLength`, `ErrInvalidSpanIDLength`, `ErrInvalidSpanIDLength`, or `ErrNilSpanID` from the `go.opentelemetry.io/otel` package are unexported now. (#1243) - The `AddEventWithTimestamp` method on the `Span` interface in `go.opentelemetry.io/otel` is removed due to its redundancy. It is replaced by using the `AddEvent` method with a `WithTimestamp` option. (#1254) - The `MockSpan` and `MockTracer` types are removed from `go.opentelemetry.io/otel/oteltest`. `Tracer` and `Span` from the same module should be used in their place instead. (#1306) - `WorkerCount` option is removed from `go.opentelemetry.io/otel/exporters/otlp`. (#1350) - Remove the following labels types: INT32, UINT32, UINT64 and FLOAT32. (#1314) ### Fixed - Rename `MergeItererator` to `MergeIterator` in the `go.opentelemetry.io/otel/label` package. (#1244) - The `go.opentelemetry.io/otel/api/global` packages global TextMapPropagator now delegates functionality to a globally set delegate for all previously returned propagators. (#1258) - Fix condition in `label.Any`. (#1299) - Fix global `TracerProvider` to pass options to its configured provider. (#1329) - Fix missing handler for `ExactKind` aggregator in OTLP metrics transformer (#1309) ## [0.13.0] - 2020-10-08 ### Added - OTLP Metric exporter supports Histogram aggregation. (#1209) - The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214) - A Baggage API to implement the OpenTelemetry specification. (#1217) - Add Shutdown method to sdk/trace/provider, shutdown processors in the order they were registered. (#1227) ### Changed - Set default propagator to no-op propagator. (#1184) - The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel/propagation` package. (#1212) (#1325) - The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212) - The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification. They now are `Unset`, `Error`, and `Ok`. They no longer track the gRPC codes. (#1214) - The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214) - Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/baggage`. (#1217) (#1325) - A `Shutdown` method of `SpanProcessor` and all its implementations receives a context and returns an error. (#1264) ### Fixed - Copies of data from arrays and slices passed to `go.opentelemetry.io/otel/label.ArrayValue()` are now used in the returned `Value` instead of using the mutable data itself. (#1226) ### Removed - The `ExtractHTTP` and `InjectHTTP` functions from the `go.opentelemetry.io/otel/api/propagation` package were removed. (#1212) - The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification. The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212) - The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216) - The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217) - Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219) - Nested array/slice support has been removed. (#1226) ## [0.12.0] - 2020-09-24 ### Added - A `SpanConfigure` function in `go.opentelemetry.io/otel/api/trace` to create a new `SpanConfig` from `SpanOption`s. (#1108) - In the `go.opentelemetry.io/otel/api/trace` package, `NewTracerConfig` was added to construct new `TracerConfig`s. This addition was made to conform with our project option conventions. (#1155) - Instrumentation library information was added to the Zipkin exporter. (#1119) - The `SpanProcessor` interface now has a `ForceFlush()` method. (#1166) - More semantic conventions for k8s as resource attributes. (#1167) ### Changed - Add reconnecting udp connection type to Jaeger exporter. This change adds a new optional implementation of the udp conn interface used to detect changes to an agent's host dns record. It then adopts the new destination address to ensure the exporter doesn't get stuck. This change was ported from jaegertracing/jaeger-client-go#520. (#1063) - Replace `StartOption` and `EndOption` in `go.opentelemetry.io/otel/api/trace` with `SpanOption`. This change is matched by replacing the `StartConfig` and `EndConfig` with a unified `SpanConfig`. (#1108) - Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`. This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108) - The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109) - Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package. This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118) - Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119) - Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115) - Move `tools` package under `internal`. (#1141) - Move `go.opentelemetry.io/otel/api/correlation` package to `go.opentelemetry.io/otel/api/baggage`. (#1142) The `correlation.CorrelationContext` propagator has been renamed `baggage.Baggage`. Other exported functions and types are unchanged. - Rename `ParentOrElse` sampler to `ParentBased` and allow setting samplers depending on parent span. (#1153) - In the `go.opentelemetry.io/otel/api/trace` package, `SpanConfigure` was renamed to `NewSpanConfig`. (#1155) - Change `dependabot.yml` to add a `Skip Changelog` label to dependabot-sourced PRs. (#1161) - The [configuration style guide](https://github.com/open-telemetry/opentelemetry-go/blob/master/CONTRIBUTING.md#config) has been updated to recommend the use of `newConfig()` instead of `configure()`. (#1163) - The `otlp.Config` type has been unexported and changed to `otlp.config`, along with its initializer. (#1163) - Ensure exported interface types include parameter names and update the Style Guide to reflect this styling rule. (#1172) - Don't consider unset environment variable for resource detection to be an error. (#1170) - Rename `go.opentelemetry.io/otel/api/metric.ConfigureInstrument` to `NewInstrumentConfig` and `go.opentelemetry.io/otel/api/metric.ConfigureMeter` to `NewMeterConfig`. - ValueObserver instruments use LastValue aggregator by default. (#1165) - OTLP Metric exporter supports LastValue aggregation. (#1165) - Move the `go.opentelemetry.io/otel/api/unit` package to `go.opentelemetry.io/otel/unit`. (#1185) - Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) - Rename `NoopProvider` to `NoopMeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) - Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metric/metrictest` package. (#1190) - Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric/registry` package. (#1190) - Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metri/registryc` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) - Rename `NoopProvider` to `NoopTracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) - Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) - Rename `WrapperProvider` to `WrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) - Rename `NewWrapperProvider` to `NewWrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) - Rename `Provider` method of the pull controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/pull` package. (#1190) - Rename `Provider` method of the push controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/push` package. (#1190) - Rename `ProviderOptions` to `TracerProviderConfig` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `ProviderOption` to `TracerProviderOption` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Renamed `SamplingDecision` values to comply with OpenTelemetry specification change. (#1192) - Renamed Zipkin attribute names from `ot.status_code & ot.status_description` to `otel.status_code & otel.status_description`. (#1201) - The default SDK now invokes registered `SpanProcessor`s in the order they were registered with the `TracerProvider`. (#1195) - Add test of spans being processed by the `SpanProcessor`s in the order they were registered. (#1203) ### Removed - Remove the B3 propagator from `go.opentelemetry.io/otel/propagators`. It is now located in the `go.opentelemetry.io/contrib/propagators/` module. (#1191) - Remove the semantic convention for HTTP status text, `HTTPStatusTextKey` from package `go.opentelemetry.io/otel/semconv`. (#1194) ### Fixed - Zipkin example no longer mentions `ParentSampler`, corrected to `ParentBased`. (#1171) - Fix missing shutdown processor in otel-collector example. (#1186) - Fix missing shutdown processor in basic and namedtracer examples. (#1197) ## [0.11.0] - 2020-08-24 ### Added - Support for exporting array-valued attributes via OTLP. (#992) - `Noop` and `InMemory` `SpanBatcher` implementations to help with testing integrations. (#994) - Support for filtering metric label sets. (#1047) - A dimensionality-reducing metric Processor. (#1057) - Integration tests for more OTel Collector Attribute types. (#1062) - A new `WithSpanProcessor` `ProviderOption` is added to the `go.opentelemetry.io/otel/sdk/trace` package to create a `Provider` and automatically register the `SpanProcessor`. (#1078) ### Changed - Rename `sdk/metric/processor/test` to `sdk/metric/processor/processortest`. (#1049) - Rename `sdk/metric/controller/test` to `sdk/metric/controller/controllertest`. (#1049) - Rename `api/testharness` to `api/apitest`. (#1049) - Rename `api/trace/testtrace` to `api/trace/tracetest`. (#1049) - Change Metric Processor to merge multiple observations. (#1024) - The `go.opentelemetry.io/otel/bridge/opentracing` bridge package has been made into its own module. This removes the package dependencies of this bridge from the rest of the OpenTelemetry based project. (#1038) - Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016) - The environment variable used for resource detection has been changed from `OTEL_RESOURCE_LABELS` to `OTEL_RESOURCE_ATTRIBUTES` (#1042) - Replace `WithSyncer` with `WithBatcher` in examples. (#1044) - Replace the `google.golang.org/grpc/codes` dependency in the API with an equivalent `go.opentelemetry.io/otel/codes` package. (#1046) - Merge the `go.opentelemetry.io/otel/api/label` and `go.opentelemetry.io/otel/api/kv` into the new `go.opentelemetry.io/otel/label` package. (#1060) - Unify Callback Function Naming. Rename `*Callback` with `*Func`. (#1061) - CI builds validate against last two versions of Go, dropping 1.13 and adding 1.15. (#1064) - The `go.opentelemetry.io/otel/sdk/export/trace` interfaces `SpanSyncer` and `SpanBatcher` have been replaced with a specification compliant `Exporter` interface. This interface still supports the export of `SpanData`, but only as a slice. Implementation are also required now to return any error from `ExportSpans` if one occurs as well as implement a `Shutdown` method for exporter clean-up. (#1078) - The `go.opentelemetry.io/otel/sdk/trace` `NewBatchSpanProcessor` function no longer returns an error. If a `nil` exporter is passed as an argument to this function, instead of it returning an error, it now returns a `BatchSpanProcessor` that handles the export of `SpanData` by not taking any action. (#1078) - The `go.opentelemetry.io/otel/sdk/trace` `NewProvider` function to create a `Provider` no longer returns an error, instead only a `*Provider`. This change is related to `NewBatchSpanProcessor` not returning an error which was the only error this function would return. (#1078) ### Removed - Duplicate, unused API sampler interface. (#999) Use the [`Sampler` interface](https://github.com/open-telemetry/opentelemetry-go/blob/v0.11.0/sdk/trace/sampling.go) provided by the SDK instead. - The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository. This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027) - The `WithSpan` method of the `Tracer` interface. The functionality this method provided was limited compared to what a user can provide themselves. It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043) - The `RegisterSpanProcessor` and `UnregisterSpanProcessor` functions. These were holdovers from an approach prior to the TracerProvider design. They were not used anymore. (#1077) - The `oterror` package. (#1026) - The `othttp` and `httptrace` instrumentations were moved to `go.opentelemetry.io/contrib`. (#1032) ### Fixed - The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031) - Correct instrumentation version tag in Jaeger exporter. (#1037) - The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043) - Move internally generated protobuf code from the `go.opentelemetry.io/otel` to the OTLP exporter to reduce dependency overhead. (#1050) - The `otel-collector` example referenced outdated collector processors. (#1006) ## [0.10.0] - 2020-07-29 This release migrates the default OpenTelemetry SDK into its own Go module, decoupling the SDK from the API and reducing dependencies for instrumentation packages. ### Added - The Zipkin exporter now has `NewExportPipeline` and `InstallNewPipeline` constructor functions to match the common pattern. These function build a new exporter with default SDK options and register the exporter with the `global` package respectively. (#944) - Add propagator option for gRPC instrumentation. (#986) - The `testtrace` package now tracks the `trace.SpanKind` for each span. (#987) ### Changed - Replace the `RegisterGlobal` `Option` in the Jaeger exporter with an `InstallNewPipeline` constructor function. This matches the other exporter constructor patterns and will register a new exporter after building it with default configuration. (#944) - The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`. This new exporter was made into its own Go module to follow the pattern of all exporters and decouple it from the `go.opentelemetry.io/otel` module. (#956, #963) - Move the `go.opentelemetry.io/otel/exporters/test` test package to `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#962) - The `go.opentelemetry.io/otel/api/kv/value` package was merged into the parent `go.opentelemetry.io/otel/api/kv` package. (#968) - `value.Bool` was replaced with `kv.BoolValue`. - `value.Int64` was replaced with `kv.Int64Value`. - `value.Uint64` was replaced with `kv.Uint64Value`. - `value.Float64` was replaced with `kv.Float64Value`. - `value.Int32` was replaced with `kv.Int32Value`. - `value.Uint32` was replaced with `kv.Uint32Value`. - `value.Float32` was replaced with `kv.Float32Value`. - `value.String` was replaced with `kv.StringValue`. - `value.Int` was replaced with `kv.IntValue`. - `value.Uint` was replaced with `kv.UintValue`. - `value.Array` was replaced with `kv.ArrayValue`. - Rename `Infer` to `Any` in the `go.opentelemetry.io/otel/api/kv` package. (#972) - Change `othttp` to use the `httpsnoop` package to wrap the `ResponseWriter` so that optional interfaces (`http.Hijacker`, `http.Flusher`, etc.) that are implemented by the original `ResponseWriter`are also implemented by the wrapped `ResponseWriter`. (#979) - Rename `go.opentelemetry.io/otel/sdk/metric/aggregator/test` package to `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest`. (#980) - Make the SDK into its own Go module called `go.opentelemetry.io/otel/sdk`. (#985) - Changed the default trace `Sampler` from `AlwaysOn` to `ParentOrElse(AlwaysOn)`. (#989) ### Removed - The `IndexedAttribute` function from the `go.opentelemetry.io/otel/api/label` package was removed in favor of `IndexedLabel` which it was synonymous with. (#970) ### Fixed - Bump github.com/golangci/golangci-lint from 1.28.3 to 1.29.0 in /tools. (#953) - Bump github.com/google/go-cmp from 0.5.0 to 0.5.1. (#957) - Use `global.Handle` for span export errors in the OTLP exporter. (#946) - Correct Go language formatting in the README documentation. (#961) - Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977) - Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983) - Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984) ## [0.9.0] - 2020-07-20 ### Added - A new Resource Detector interface is included to allow resources to be automatically detected and included. (#939) - A Detector to automatically detect resources from an environment variable. (#939) - Github action to generate protobuf Go bindings locally in `internal/opentelemetry-proto-gen`. (#938) - OTLP .proto files from `open-telemetry/opentelemetry-proto` imported as a git submodule under `internal/opentelemetry-proto`. References to `github.com/open-telemetry/opentelemetry-proto` changed to `go.opentelemetry.io/otel/internal/opentelemetry-proto-gen`. (#942) ### Changed - Non-nil value `struct`s for key-value pairs will be marshalled using JSON rather than `Sprintf`. (#948) ### Removed - Removed dependency on `github.com/open-telemetry/opentelemetry-collector`. (#943) ## [0.8.0] - 2020-07-09 ### Added - The `B3Encoding` type to represent the B3 encoding(s) the B3 propagator can inject. A value for HTTP supported encodings (Multiple Header: `MultipleHeader`, Single Header: `SingleHeader`) are included. (#882) - The `FlagsDeferred` trace flag to indicate if the trace sampling decision has been deferred. (#882) - The `FlagsDebug` trace flag to indicate if the trace is a debug trace. (#882) - Add `peer.service` semantic attribute. (#898) - Add database-specific semantic attributes. (#899) - Add semantic convention for `faas.coldstart` and `container.id`. (#909) - Add http content size semantic conventions. (#905) - Include `http.request_content_length` in HTTP request basic attributes. (#905) - Add semantic conventions for operating system process resource attribute keys. (#919) - The Jaeger exporter now has a `WithBatchMaxCount` option to specify the maximum number of spans sent in a batch. (#931) ### Changed - Update `CONTRIBUTING.md` to ask for updates to `CHANGELOG.md` with each pull request. (#879) - Use lowercase header names for B3 Multiple Headers. (#881) - The B3 propagator `SingleHeader` field has been replaced with `InjectEncoding`. This new field can be set to combinations of the `B3Encoding` bitmasks and will inject trace information in these encodings. If no encoding is set, the propagator will default to `MultipleHeader` encoding. (#882) - The B3 propagator now extracts from either HTTP encoding of B3 (Single Header or Multiple Header) based on what is contained in the header. Preference is given to Single Header encoding with Multiple Header being the fallback if Single Header is not found or is invalid. This behavior change is made to dynamically support all correctly encoded traces received instead of having to guess the expected encoding prior to receiving. (#882) - Extend semantic conventions for RPC. (#900) - To match constant naming conventions in the `api/standard` package, the `FaaS*` key names are appended with a suffix of `Key`. (#920) - `"api/standard".FaaSName` -> `FaaSNameKey` - `"api/standard".FaaSID` -> `FaaSIDKey` - `"api/standard".FaaSVersion` -> `FaaSVersionKey` - `"api/standard".FaaSInstance` -> `FaaSInstanceKey` ### Removed - The `FlagsUnused` trace flag is removed. The purpose of this flag was to act as the inverse of `FlagsSampled`, the inverse of `FlagsSampled` is used instead. (#882) - The B3 header constants (`B3SingleHeader`, `B3DebugFlagHeader`, `B3TraceIDHeader`, `B3SpanIDHeader`, `B3SampledHeader`, `B3ParentSpanIDHeader`) are removed. If B3 header keys are needed [the authoritative OpenZipkin package constants](https://pkg.go.dev/github.com/openzipkin/zipkin-go@v0.2.2/propagation/b3?tab=doc#pkg-constants) should be used instead. (#882) ### Fixed - The B3 Single Header name is now correctly `b3` instead of the previous `X-B3`. (#881) - The B3 propagator now correctly supports sampling only values (`b3: 0`, `b3: 1`, or `b3: d`) for a Single B3 Header. (#882) - The B3 propagator now propagates the debug flag. This removes the behavior of changing the debug flag into a set sampling bit. Instead, this now follow the B3 specification and omits the `X-B3-Sampling` header. (#882) - The B3 propagator now tracks "unset" sampling state (meaning "defer the decision") and does not set the `X-B3-Sampling` header when injecting. (#882) - Bump github.com/itchyny/gojq from 0.10.3 to 0.10.4 in /tools. (#883) - Bump github.com/opentracing/opentracing-go from v1.1.1-0.20190913142402-a7454ce5950e to v1.2.0. (#885) - The tracing time conversion for OTLP spans is now correctly set to `UnixNano`. (#896) - Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908) - Ensure `httptrace.clientTracer` closes `http.headers` span. (#912) - Prometheus exporter will not apply stale updates or forget inactive metrics. (#903) - Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905) - Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913) - Update otel-colector example to use the v0.5.0 collector. (#915) - The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922) - The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922) - The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists. This is in accordance with OpenTelemetry semantic conventions. (#922) - Correlation Context extractor will no longer insert an empty map into the returned context when no valid values are extracted. (#923) - Bump google.golang.org/api from 0.28.0 to 0.29.0 in /exporters/trace/jaeger. (#925) - Bump github.com/itchyny/gojq from 0.10.4 to 0.11.0 in /tools. (#926) - Bump github.com/golangci/golangci-lint from 1.28.1 to 1.28.2 in /tools. (#930) ## [0.7.0] - 2020-06-26 This release implements the v0.5.0 version of the OpenTelemetry specification. ### Added - The othttp instrumentation now includes default metrics. (#861) - This CHANGELOG file to track all changes in the project going forward. - Support for array type attributes. (#798) - Apply transitive dependabot go.mod dependency updates as part of a new automatic Github workflow. (#844) - Timestamps are now passed to exporters for each export. (#835) - Add new `Accumulation` type to metric SDK to transport telemetry from `Accumulator`s to `Processor`s. This replaces the prior `Record` `struct` use for this purpose. (#835) - New dependabot integration to automate package upgrades. (#814) - `Meter` and `Tracer` implementations accept instrumentation version version as an optional argument. This instrumentation version is passed on to exporters. (#811) (#805) (#802) - The OTLP exporter includes the instrumentation version in telemetry it exports. (#811) - Environment variables for Jaeger exporter are supported. (#796) - New `aggregation.Kind` in the export metric API. (#808) - New example that uses OTLP and the collector. (#790) - Handle errors in the span `SetName` during span initialization. (#791) - Default service config to enable retries for retry-able failed requests in the OTLP exporter and an option to override this default. (#777) - New `go.opentelemetry.io/otel/api/oterror` package to uniformly support error handling and definitions for the project. (#778) - New `global` default implementation of the `go.opentelemetry.io/otel/api/oterror.Handler` interface to be used to handle errors prior to an user defined `Handler`. There is also functionality for the user to register their `Handler` as well as a convenience function `Handle` to handle an error with this global `Handler`(#778) - Options to specify propagators for httptrace and grpctrace instrumentation. (#784) - The required `application/json` header for the Zipkin exporter is included in all exports. (#774) - Integrate HTTP semantics helpers from the contrib repository into the `api/standard` package. #769 ### Changed - Rename `Integrator` to `Processor` in the metric SDK. (#863) - Rename `AggregationSelector` to `AggregatorSelector`. (#859) - Rename `SynchronizedCopy` to `SynchronizedMove`. (#858) - Rename `simple` integrator to `basic` integrator. (#857) - Merge otlp collector examples. (#841) - Change the metric SDK to support cumulative, delta, and pass-through exporters directly. With these changes, cumulative and delta specific exporters are able to request the correct kind of aggregation from the SDK. (#840) - The `Aggregator.Checkpoint` API is renamed to `SynchronizedCopy` and adds an argument, a different `Aggregator` into which the copy is stored. (#812) - The `export.Aggregator` contract is that `Update()` and `SynchronizedCopy()` are synchronized with each other. All the aggregation interfaces (`Sum`, `LastValue`, ...) are not meant to be synchronized, as the caller is expected to synchronize aggregators at a higher level after the `Accumulator`. Some of the `Aggregators` used unnecessary locking and that has been cleaned up. (#812) - Use of `metric.Number` was replaced by `int64` now that we use `sync.Mutex` in the `MinMaxSumCount` and `Histogram` `Aggregators`. (#812) - Replace `AlwaysParentSample` with `ParentSample(fallback)` to match the OpenTelemetry v0.5.0 specification. (#810) - Rename `sdk/export/metric/aggregator` to `sdk/export/metric/aggregation`. #808 - Send configured headers with every request in the OTLP exporter, instead of just on connection creation. (#806) - Update error handling for any one off error handlers, replacing, instead, with the `global.Handle` function. (#791) - Rename `plugin` directory to `instrumentation` to match the OpenTelemetry specification. (#779) - Makes the argument order to Histogram and DDSketch `New()` consistent. (#781) ### Removed - `Uint64NumberKind` and related functions from the API. (#864) - Context arguments from `Aggregator.Checkpoint` and `Integrator.Process` as they were unused. (#803) - `SpanID` is no longer included in parameters for sampling decision to match the OpenTelemetry specification. (#775) ### Fixed - Upgrade OTLP exporter to opentelemetry-proto matching the opentelemetry-collector v0.4.0 release. (#866) - Allow changes to `go.sum` and `go.mod` when running dependabot tidy-up. (#871) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1. (#824) - Bump github.com/prometheus/client_golang from 1.7.0 to 1.7.1 in /exporters/metric/prometheus. (#867) - Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/jaeger. (#853) - Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/zipkin. (#854) - Bumps github.com/golang/protobuf from 1.3.2 to 1.4.2 (#848) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/otlp (#817) - Bump github.com/golangci/golangci-lint from 1.25.1 to 1.27.0 in /tools (#828) - Bump github.com/prometheus/client_golang from 1.5.0 to 1.7.0 in /exporters/metric/prometheus (#838) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/jaeger (#829) - Bump github.com/benbjohnson/clock from 1.0.0 to 1.0.3 (#815) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/zipkin (#823) - Bump github.com/itchyny/gojq from 0.10.1 to 0.10.3 in /tools (#830) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/metric/prometheus (#822) - Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/zipkin (#820) - Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/jaeger (#831) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 (#836) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/trace/jaeger (#837) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/otlp (#839) - Bump google.golang.org/api from 0.20.0 to 0.28.0 in /exporters/trace/jaeger (#843) - Set span status from HTTP status code in the othttp instrumentation. (#832) - Fixed typo in push controller comment. (#834) - The `Aggregator` testing has been updated and cleaned. (#812) - `metric.Number(0)` expressions are replaced by `0` where possible. (#812) - Fixed `global` `handler_test.go` test failure. #804 - Fixed `BatchSpanProcessor.Shutdown` to wait until all spans are processed. (#766) - Fixed OTLP example's accidental early close of exporter. (#807) - Ensure zipkin exporter reads and closes response body. (#788) - Update instrumentation to use `api/standard` keys instead of custom keys. (#782) - Clean up tools and RELEASING documentation. (#762) ## [0.6.0] - 2020-05-21 ### Added - Support for `Resource`s in the prometheus exporter. (#757) - New pull controller. (#751) - New `UpDownSumObserver` instrument. (#750) - OpenTelemetry collector demo. (#711) - New `SumObserver` instrument. (#747) - New `UpDownCounter` instrument. (#745) - New timeout `Option` and configuration function `WithTimeout` to the push controller. (#742) - New `api/standards` package to implement semantic conventions and standard key-value generation. (#731) ### Changed - Rename `Register*` functions in the metric API to `New*` for all `Observer` instruments. (#761) - Use `[]float64` for histogram boundaries, not `[]metric.Number`. (#758) - Change OTLP example to use exporter as a trace `Syncer` instead of as an unneeded `Batcher`. (#756) - Replace `WithResourceAttributes()` with `WithResource()` in the trace SDK. (#754) - The prometheus exporter now uses the new pull controller. (#751) - Rename `ScheduleDelayMillis` to `BatchTimeout` in the trace `BatchSpanProcessor`.(#752) - Support use of synchronous instruments in asynchronous callbacks (#725) - Move `Resource` from the `Export` method parameter into the metric export `Record`. (#739) - Rename `Observer` instrument to `ValueObserver`. (#734) - The push controller now has a method (`Provider()`) to return a `metric.Provider` instead of the old `Meter` method that acted as a `metric.Provider`. (#738) - Replace `Measure` instrument by `ValueRecorder` instrument. (#732) - Rename correlation context header from `"Correlation-Context"` to `"otcorrelations"` to match the OpenTelemetry specification. (#727) ### Fixed - Ensure gRPC `ClientStream` override methods do not panic in grpctrace package. (#755) - Disable parts of `BatchSpanProcessor` test until a fix is found. (#743) - Fix `string` case in `kv` `Infer` function. (#746) - Fix panic in grpctrace client interceptors. (#740) - Refactor the `api/metrics` push controller and add `CheckpointSet` synchronization. (#737) - Rewrite span batch process queue batching logic. (#719) - Remove the push controller named Meter map. (#738) - Fix Histogram aggregator initial state (fix #735). (#736) - Ensure golang alpine image is running `golang-1.14` for examples. (#733) - Added test for grpctrace `UnaryInterceptorClient`. (#695) - Rearrange `api/metric` code layout. (#724) ## [0.5.0] - 2020-05-13 ### Added - Batch `Observer` callback support. (#717) - Alias `api` types to root package of project. (#696) - Create basic `othttp.Transport` for simple client instrumentation. (#678) - `SetAttribute(string, interface{})` to the trace API. (#674) - Jaeger exporter option that allows user to specify custom http client. (#671) - `Stringer` and `Infer` methods to `key`s. (#662) ### Changed - Rename `NewKey` in the `kv` package to just `Key`. (#721) - Move `core` and `key` to `kv` package. (#720) - Make the metric API `Meter` a `struct` so the abstract `MeterImpl` can be passed and simplify implementation. (#709) - Rename SDK `Batcher` to `Integrator` to match draft OpenTelemetry SDK specification. (#710) - Rename SDK `Ungrouped` integrator to `simple.Integrator` to match draft OpenTelemetry SDK specification. (#710) - Rename SDK `SDK` `struct` to `Accumulator` to match draft OpenTelemetry SDK specification. (#710) - Move `Number` from `core` to `api/metric` package. (#706) - Move `SpanContext` from `core` to `trace` package. (#692) - Change traceparent header from `Traceparent` to `traceparent` to implement the W3C specification. (#681) ### Fixed - Update tooling to run generators in all submodules. (#705) - gRPC interceptor regexp to match methods without a service name. (#683) - Use a `const` for padding 64-bit B3 trace IDs. (#701) - Update `mockZipkin` listen address from `:0` to `127.0.0.1:0`. (#700) - Left-pad 64-bit B3 trace IDs with zero. (#698) - Propagate at least the first W3C tracestate header. (#694) - Remove internal `StateLocker` implementation. (#688) - Increase instance size CI system uses. (#690) - Add a `key` benchmark and use reflection in `key.Infer()`. (#679) - Fix internal `global` test by using `global.Meter` with `RecordBatch()`. (#680) - Reimplement histogram using mutex instead of `StateLocker`. (#669) - Switch `MinMaxSumCount` to a mutex lock implementation instead of `StateLocker`. (#667) - Update documentation to not include any references to `WithKeys`. (#672) - Correct misspelling. (#668) - Fix clobbering of the span context if extraction fails. (#656) - Bump `golangci-lint` and work around the corrupting bug. (#666) (#670) ## [0.4.3] - 2020-04-24 ### Added - `Dockerfile` and `docker-compose.yml` to run example code. (#635) - New `grpctrace` package that provides gRPC client and server interceptors for both unary and stream connections. (#621) - New `api/label` package, providing common label set implementation. (#651) - Support for JSON marshaling of `Resources`. (#654) - `TraceID` and `SpanID` implementations for `Stringer` interface. (#642) - `RemoteAddrKey` in the othttp plugin to include the HTTP client address in top-level spans. (#627) - `WithSpanFormatter` option to the othttp plugin. (#617) - Updated README to include section for compatible libraries and include reference to the contrib repository. (#612) - The prometheus exporter now supports exporting histograms. (#601) - A `String` method to the `Resource` to return a hashable identifier for a now unique resource. (#613) - An `Iter` method to the `Resource` to return an array `AttributeIterator`. (#613) - An `Equal` method to the `Resource` test the equivalence of resources. (#613) - An iterable structure (`AttributeIterator`) for `Resource` attributes. ### Changed - zipkin export's `NewExporter` now requires a `serviceName` argument to ensure this needed values is provided. (#644) - Pass `Resources` through the metrics export pipeline. (#659) ### Removed - `WithKeys` option from the metric API. (#639) ### Fixed - Use the `label.Set.Equivalent` value instead of an encoding in the batcher. (#658) - Correct typo `trace.Exporter` to `trace.SpanSyncer` in comments. (#653) - Use type names for return values in jaeger exporter. (#648) - Increase the visibility of the `api/key` package by updating comments and fixing usages locally. (#650) - `Checkpoint` only after `Update`; Keep records in the `sync.Map` longer. (#647) - Do not cache `reflect.ValueOf()` in metric Labels. (#649) - Batch metrics exported from the OTLP exporter based on `Resource` and labels. (#626) - Add error wrapping to the prometheus exporter. (#631) - Update the OTLP exporter batching of traces to use a unique `string` representation of an associated `Resource` as the batching key. (#623) - Update OTLP `SpanData` transform to only include the `ParentSpanID` if one exists. (#614) - Update `Resource` internal representation to uniquely and reliably identify resources. (#613) - Check return value from `CheckpointSet.ForEach` in prometheus exporter. (#622) - Ensure spans created by httptrace client tracer reflect operation structure. (#618) - Create a new recorder rather than reuse when multiple observations in same epoch for asynchronous instruments. #610 - The default port the OTLP exporter uses to connect to the OpenTelemetry collector is updated to match the one the collector listens on by default. (#611) ## [0.4.2] - 2020-03-31 ### Fixed - Fix `pre_release.sh` to update version in `sdk/opentelemetry.go`. (#607) - Fix time conversion from internal to OTLP in OTLP exporter. (#606) ## [0.4.1] - 2020-03-31 ### Fixed - Update `tag.sh` to create signed tags. (#604) ## [0.4.0] - 2020-03-30 ### Added - New API package `api/metric/registry` that exposes a `MeterImpl` wrapper for use by SDKs to generate unique instruments. (#580) - Script to verify examples after a new release. (#579) ### Removed - The dogstatsd exporter due to lack of support. This additionally removes support for statsd. (#591) - `LabelSet` from the metric API. This is replaced by a `[]core.KeyValue` slice. (#595) - `Labels` from the metric API's `Meter` interface. (#595) ### Changed - The metric `export.Labels` became an interface which the SDK implements and the `export` package provides a simple, immutable implementation of this interface intended for testing purposes. (#574) - Renamed `internal/metric.Meter` to `MeterImpl`. (#580) - Renamed `api/global/internal.obsImpl` to `asyncImpl`. (#580) ### Fixed - Corrected missing return in mock span. (#582) - Update License header for all source files to match CNCF guidelines and include a test to ensure it is present. (#586) (#596) - Update to v0.3.0 of the OTLP in the OTLP exporter. (#588) - Update pre-release script to be compatible between GNU and BSD based systems. (#592) - Add a `RecordBatch` benchmark. (#594) - Moved span transforms of the OTLP exporter to the internal package. (#593) - Build both go-1.13 and go-1.14 in circleci to test for all supported versions of Go. (#569) - Removed unneeded allocation on empty labels in OLTP exporter. (#597) - Update `BatchedSpanProcessor` to process the queue until no data but respect max batch size. (#599) - Update project documentation godoc.org links to pkg.go.dev. (#602) ## [0.3.0] - 2020-03-21 This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality. There is still a possibility of breaking changes. ### Added - Add `Observer` metric instrument. (#474) - Add global `Propagators` functionality to enable deferred initialization for propagators registered before the first Meter SDK is installed. (#494) - Simplified export setup pipeline for the jaeger exporter to match other exporters. (#459) - The zipkin trace exporter. (#495) - The OTLP exporter to export metric and trace telemetry to the OpenTelemetry collector. (#497) (#544) (#545) - Add `StatusMessage` field to the trace `Span`. (#524) - Context propagation in OpenTracing bridge in terms of OpenTelemetry context propagation. (#525) - The `Resource` type was added to the SDK. (#528) - The global API now supports a `Tracer` and `Meter` function as shortcuts to getting a global `*Provider` and calling these methods directly. (#538) - The metric API now defines a generic `MeterImpl` interface to support general purpose `Meter` construction. Additionally, `SyncImpl` and `AsyncImpl` are added to support general purpose instrument construction. (#560) - A metric `Kind` is added to represent the `MeasureKind`, `ObserverKind`, and `CounterKind`. (#560) - Scripts to better automate the release process. (#576) ### Changed - Default to to use `AlwaysSampler` instead of `ProbabilitySampler` to match OpenTelemetry specification. (#506) - Renamed `AlwaysSampleSampler` to `AlwaysOnSampler` in the trace API. (#511) - Renamed `NeverSampleSampler` to `AlwaysOffSampler` in the trace API. (#511) - The `Status` field of the `Span` was changed to `StatusCode` to disambiguate with the added `StatusMessage`. (#524) - Updated the trace `Sampler` interface conform to the OpenTelemetry specification. (#531) - Rename metric API `Options` to `Config`. (#541) - Rename metric `Counter` aggregator to be `Sum`. (#541) - Unify metric options into `Option` from instrument specific options. (#541) - The trace API's `TraceProvider` now support `Resource`s. (#545) - Correct error in zipkin module name. (#548) - The jaeger trace exporter now supports `Resource`s. (#551) - Metric SDK now supports `Resource`s. The `WithResource` option was added to configure a `Resource` on creation and the `Resource` method was added to the metric `Descriptor` to return the associated `Resource`. (#552) - Replace `ErrNoLastValue` and `ErrEmptyDataSet` by `ErrNoData` in the metric SDK. (#557) - The stdout trace exporter now supports `Resource`s. (#558) - The metric `Descriptor` is now included at the API instead of the SDK. (#560) - Replace `Ordered` with an iterator in `export.Labels`. (#567) ### Removed - The vendor specific Stackdriver. It is now hosted on 3rd party vendor infrastructure. (#452) - The `Unregister` method for metric observers as it is not in the OpenTelemetry specification. (#560) - `GetDescriptor` from the metric SDK. (#575) - The `Gauge` instrument from the metric API. (#537) ### Fixed - Make histogram aggregator checkpoint consistent. (#438) - Update README with import instructions and how to build and test. (#505) - The default label encoding was updated to be unique. (#508) - Use `NewRoot` in the othttp plugin for public endpoints. (#513) - Fix data race in `BatchedSpanProcessor`. (#518) - Skip test-386 for Mac OS 10.15.x (Catalina and upwards). #521 - Use a variable-size array to represent ordered labels in maps. (#523) - Update the OTLP protobuf and update changed import path. (#532) - Use `StateLocker` implementation in `MinMaxSumCount`. (#546) - Eliminate goroutine leak in histogram stress test. (#547) - Update OTLP exporter with latest protobuf. (#550) - Add filters to the othttp plugin. (#556) - Provide an implementation of the `Header*` filters that do not depend on Go 1.14. (#565) - Encode labels once during checkpoint. The checkpoint function is executed in a single thread so we can do the encoding lazily before passing the encoded version of labels to the exporter. This is a cheap and quick way to avoid encoding the labels on every collection interval. (#572) - Run coverage over all packages in `COVERAGE_MOD_DIR`. (#573) ## [0.2.3] - 2020-03-04 ### Added - `RecordError` method on `Span`s in the trace API to Simplify adding error events to spans. (#473) - Configurable push frequency for exporters setup pipeline. (#504) ### Changed - Rename the `exporter` directory to `exporters`. The `go.opentelemetry.io/otel/exporter/trace/jaeger` package was mistakenly released with a `v1.0.0` tag instead of `v0.1.0`. This resulted in all subsequent releases not becoming the default latest. A consequence of this was that all `go get`s pulled in the incompatible `v0.1.0` release of that package when pulling in more recent packages from other otel packages. Renaming the `exporter` directory to `exporters` fixes this issue by renaming the package and therefore clearing any existing dependency tags. Consequentially, this action also renames *all* exporter packages. (#502) ### Removed - The `CorrelationContextHeader` constant in the `correlation` package is no longer exported. (#503) ## [0.2.2] - 2020-02-27 ### Added - `HTTPSupplier` interface in the propagation API to specify methods to retrieve and store a single value for a key to be associated with a carrier. (#467) - `HTTPExtractor` interface in the propagation API to extract information from an `HTTPSupplier` into a context. (#467) - `HTTPInjector` interface in the propagation API to inject information into an `HTTPSupplier.` (#467) - `Config` and configuring `Option` to the propagator API. (#467) - `Propagators` interface in the propagation API to contain the set of injectors and extractors for all supported carrier formats. (#467) - `HTTPPropagator` interface in the propagation API to inject and extract from an `HTTPSupplier.` (#467) - `WithInjectors` and `WithExtractors` functions to the propagator API to configure injectors and extractors to use. (#467) - `ExtractHTTP` and `InjectHTTP` functions to apply configured HTTP extractors and injectors to a passed context. (#467) - Histogram aggregator. (#433) - `DefaultPropagator` function and have it return `trace.TraceContext` as the default context propagator. (#456) - `AlwaysParentSample` sampler to the trace API. (#455) - `WithNewRoot` option function to the trace API to specify the created span should be considered a root span. (#451) ### Changed - Renamed `WithMap` to `ContextWithMap` in the correlation package. (#481) - Renamed `FromContext` to `MapFromContext` in the correlation package. (#481) - Move correlation context propagation to correlation package. (#479) - Do not default to putting remote span context into links. (#480) - `Tracer.WithSpan` updated to accept `StartOptions`. (#472) - Renamed `MetricKind` to `Kind` to not stutter in the type usage. (#432) - Renamed the `export` package to `metric` to match directory structure. (#432) - Rename the `api/distributedcontext` package to `api/correlation`. (#444) - Rename the `api/propagators` package to `api/propagation`. (#444) - Move the propagators from the `propagators` package into the `trace` API package. (#444) - Update `Float64Gauge`, `Int64Gauge`, `Float64Counter`, `Int64Counter`, `Float64Measure`, and `Int64Measure` metric methods to use value receivers instead of pointers. (#462) - Moved all dependencies of tools package to a tools directory. (#466) ### Removed - Binary propagators. (#467) - NOOP propagator. (#467) ### Fixed - Upgraded `github.com/golangci/golangci-lint` from `v1.21.0` to `v1.23.6` in `tools/`. (#492) - Fix a possible nil-dereference crash (#478) - Correct comments for `InstallNewPipeline` in the stdout exporter. (#483) - Correct comments for `InstallNewPipeline` in the dogstatsd exporter. (#484) - Correct comments for `InstallNewPipeline` in the prometheus exporter. (#482) - Initialize `onError` based on `Config` in prometheus exporter. (#486) - Correct module name in prometheus exporter README. (#475) - Removed tracer name prefix from span names. (#430) - Fix `aggregator_test.go` import package comment. (#431) - Improved detail in stdout exporter. (#436) - Fix a dependency issue (generate target should depend on stringer, not lint target) in Makefile. (#442) - Reorders the Makefile targets within `precommit` target so we generate files and build the code before doing linting, so we can get much nicer errors about syntax errors from the compiler. (#442) - Reword function documentation in gRPC plugin. (#446) - Send the `span.kind` tag to Jaeger from the jaeger exporter. (#441) - Fix `metadataSupplier` in the jaeger exporter to overwrite the header if existing instead of appending to it. (#441) - Upgraded to Go 1.13 in CI. (#465) - Correct opentelemetry.io URL in trace SDK documentation. (#464) - Refactored reference counting logic in SDK determination of stale records. (#468) - Add call to `runtime.Gosched` in instrument `acquireHandle` logic to not block the collector. (#469) ## [0.2.1.1] - 2020-01-13 ### Fixed - Use stateful batcher on Prometheus exporter fixing regresion introduced in #395. (#428) ## [0.2.1] - 2020-01-08 ### Added - Global meter forwarding implementation. This enables deferred initialization for metric instruments registered before the first Meter SDK is installed. (#392) - Global trace forwarding implementation. This enables deferred initialization for tracers registered before the first Trace SDK is installed. (#406) - Standardize export pipeline creation in all exporters. (#395) - A testing, organization, and comments for 64-bit field alignment. (#418) - Script to tag all modules in the project. (#414) ### Changed - Renamed `propagation` package to `propagators`. (#362) - Renamed `B3Propagator` propagator to `B3`. (#362) - Renamed `TextFormatPropagator` propagator to `TextFormat`. (#362) - Renamed `BinaryPropagator` propagator to `Binary`. (#362) - Renamed `BinaryFormatPropagator` propagator to `BinaryFormat`. (#362) - Renamed `NoopTextFormatPropagator` propagator to `NoopTextFormat`. (#362) - Renamed `TraceContextPropagator` propagator to `TraceContext`. (#362) - Renamed `SpanOption` to `StartOption` in the trace API. (#369) - Renamed `StartOptions` to `StartConfig` in the trace API. (#369) - Renamed `EndOptions` to `EndConfig` in the trace API. (#369) - `Number` now has a pointer receiver for its methods. (#375) - Renamed `CurrentSpan` to `SpanFromContext` in the trace API. (#379) - Renamed `SetCurrentSpan` to `ContextWithSpan` in the trace API. (#379) - Renamed `Message` in Event to `Name` in the trace API. (#389) - Prometheus exporter no longer aggregates metrics, instead it only exports them. (#385) - Renamed `HandleImpl` to `BoundInstrumentImpl` in the metric API. (#400) - Renamed `Float64CounterHandle` to `Float64CounterBoundInstrument` in the metric API. (#400) - Renamed `Int64CounterHandle` to `Int64CounterBoundInstrument` in the metric API. (#400) - Renamed `Float64GaugeHandle` to `Float64GaugeBoundInstrument` in the metric API. (#400) - Renamed `Int64GaugeHandle` to `Int64GaugeBoundInstrument` in the metric API. (#400) - Renamed `Float64MeasureHandle` to `Float64MeasureBoundInstrument` in the metric API. (#400) - Renamed `Int64MeasureHandle` to `Int64MeasureBoundInstrument` in the metric API. (#400) - Renamed `Release` method for bound instruments in the metric API to `Unbind`. (#400) - Renamed `AcquireHandle` method for bound instruments in the metric API to `Bind`. (#400) - Renamed the `File` option in the stdout exporter to `Writer`. (#404) - Renamed all `Options` to `Config` for all metric exports where this wasn't already the case. ### Fixed - Aggregator import path corrected. (#421) - Correct links in README. (#368) - The README was updated to match latest code changes in its examples. (#374) - Don't capitalize error statements. (#375) - Fix ignored errors. (#375) - Fix ambiguous variable naming. (#375) - Removed unnecessary type casting. (#375) - Use named parameters. (#375) - Updated release schedule. (#378) - Correct http-stackdriver example module name. (#394) - Removed the `http.request` span in `httptrace` package. (#397) - Add comments in the metrics SDK (#399) - Initialize checkpoint when creating ddsketch aggregator to prevent panic when merging into a empty one. (#402) (#403) - Add documentation of compatible exporters in the README. (#405) - Typo fix. (#408) - Simplify span check logic in SDK tracer implementation. (#419) ## [0.2.0] - 2019-12-03 ### Added - Unary gRPC tracing example. (#351) - Prometheus exporter. (#334) - Dogstatsd metrics exporter. (#326) ### Changed - Rename `MaxSumCount` aggregation to `MinMaxSumCount` and add the `Min` interface for this aggregation. (#352) - Rename `GetMeter` to `Meter`. (#357) - Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) - Rename `HTTPB3Propagator` to `B3Propagator`. (#355) - Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) - Move `/global` package to `/api/global`. (#356) - Rename `GetTracer` to `Tracer`. (#347) ### Removed - `SetAttribute` from the `Span` interface in the trace API. (#361) - `AddLink` from the `Span` interface in the trace API. (#349) - `Link` from the `Span` interface in the trace API. (#349) ### Fixed - Exclude example directories from coverage report. (#365) - Lint make target now implements automatic fixes with `golangci-lint` before a second run to report the remaining issues. (#360) - Drop `GO111MODULE` environment variable in Makefile as Go 1.13 is the project specified minimum version and this is environment variable is not needed for that version of Go. (#359) - Run the race checker for all test. (#354) - Redundant commands in the Makefile are removed. (#354) - Split the `generate` and `lint` targets of the Makefile. (#354) - Renames `circle-ci` target to more generic `ci` in Makefile. (#354) - Add example Prometheus binary to gitignore. (#358) - Support negative numbers with the `MaxSumCount`. (#335) - Resolve race conditions in `push_test.go` identified in #339. (#340) - Use `/usr/bin/env bash` as a shebang in scripts rather than `/bin/bash`. (#336) - Trace benchmark now tests both `AlwaysSample` and `NeverSample`. Previously it was testing `AlwaysSample` twice. (#325) - Trace benchmark now uses a `[]byte` for `TraceID` to fix failing test. (#325) - Added a trace benchmark to test variadic functions in `setAttribute` vs `setAttributes` (#325) - The `defaultkeys` batcher was only using the encoded label set as its map key while building a checkpoint. This allowed distinct label sets through, but any metrics sharing a label set could be overwritten or merged incorrectly. This was corrected. (#333) ## [0.1.2] - 2019-11-18 ### Fixed - Optimized the `simplelru` map for attributes to reduce the number of allocations. (#328) - Removed unnecessary unslicing of parameters that are already a slice. (#324) ## [0.1.1] - 2019-11-18 This release contains a Metrics SDK with stdout exporter and supports basic aggregations such as counter, gauges, array, maxsumcount, and ddsketch. ### Added - Metrics stdout export pipeline. (#265) - Array aggregation for raw measure metrics. (#282) - The core.Value now have a `MarshalJSON` method. (#281) ### Removed - `WithService`, `WithResources`, and `WithComponent` methods of tracers. (#314) - Prefix slash in `Tracer.Start()` for the Jaeger example. (#292) ### Changed - Allocation in LabelSet construction to reduce GC overhead. (#318) - `trace.WithAttributes` to append values instead of replacing (#315) - Use a formula for tolerance in sampling tests. (#298) - Move export types into trace and metric-specific sub-directories. (#289) - `SpanKind` back to being based on an `int` type. (#288) ### Fixed - URL to OpenTelemetry website in README. (#323) - Name of othttp default tracer. (#321) - `ExportSpans` for the stackdriver exporter now handles `nil` context. (#294) - CI modules cache to correctly restore/save from/to the cache. (#316) - Fix metric SDK race condition between `LoadOrStore` and the assignment `rec.recorder = i.meter.exporter.AggregatorFor(rec)`. (#293) - README now reflects the new code structure introduced with these changes. (#291) - Make the basic example work. (#279) ## [0.1.0] - 2019-11-04 This is the first release of open-telemetry go library. It contains api and sdk for trace and meter. ### Added - Initial OpenTelemetry trace and metric API prototypes. - Initial OpenTelemetry trace, metric, and export SDK packages. - A wireframe bridge to support compatibility with OpenTracing. - Example code for a basic, http-stackdriver, http, jaeger, and named tracer setup. - Exporters for Jaeger, Stackdriver, and stdout. - Propagators for binary, B3, and trace-context protocols. - Project information and guidelines in the form of a README and CONTRIBUTING. - Tools to build the project and a Makefile to automate the process. - Apache-2.0 license. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. [Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.1.0...HEAD [1.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.1.0 [1.0.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.1 [Metrics 0.24.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.24.0 [1.0.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0 [1.0.0-RC3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC3 [1.0.0-RC2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC2 [Experimental Metrics v0.22.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.22.0 [1.0.0-RC1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC1 [0.20.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.20.0 [0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.19.0 [0.18.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.18.0 [0.17.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.17.0 [0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.16.0 [0.15.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.15.0 [0.14.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.14.0 [0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.13.0 [0.12.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.12.0 [0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.11.0 [0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.10.0 [0.9.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.9.0 [0.8.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.8.0 [0.7.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.7.0 [0.6.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.6.0 [0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.5.0 [0.4.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.3 [0.4.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.2 [0.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.1 [0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.0 [0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.3.0 [0.2.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.3 [0.2.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.2 [0.2.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1.1 [0.2.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1 [0.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.0 [0.1.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.2 [0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1 [0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0 golang-opentelemetry-otel-1.1.0/CODEOWNERS000066400000000000000000000010051414226744000202060ustar00rootroot00000000000000##################################################### # # List of approvers for this repository # ##################################################### # # Learn about membership in OpenTelemetry community: # https://github.com/open-telemetry/community/blob/main/community-membership.md # # # Learn about CODEOWNERS file format: # https://help.github.com/en/articles/about-code-owners # * @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared CODEOWNERS @MrAlias @Aneurysm9 golang-opentelemetry-otel-1.1.0/CONTRIBUTING.md000066400000000000000000000364611414226744000210620ustar00rootroot00000000000000# Contributing to opentelemetry-go The Go special interest group (SIG) meets regularly. See the OpenTelemetry [community](https://github.com/open-telemetry/community#golang-sdk) repo for information on this and other language SIGs. See the [public meeting notes](https://docs.google.com/document/d/1A63zSWX0x2CyCK_LoNhmQC4rqhLpYXJzXbEPDUQ2n6w/edit#heading=h.9tngw7jdwd6b) for a summary description of past meetings. To request edit access, join the meeting or get in touch on [Slack](https://cloud-native.slack.com/archives/C01NPAXACKT). ## Development You can view and edit the source code by cloning this repository: ```sh git clone https://github.com/open-telemetry/opentelemetry-go.git ``` Run `make test` to run the tests instead of `go test`. There are some generated files checked into the repo. To make sure that the generated files are up-to-date, run `make` (or `make precommit` - the `precommit` target is the default). The `precommit` target also fixes the formatting of the code and checks the status of the go module files. If after running `make precommit` the output of `git status` contains `nothing to commit, working tree clean` then it means that everything is up-to-date and properly formatted. ## Pull Requests ### How to Send Pull Requests Everyone is welcome to contribute code to `opentelemetry-go` via GitHub pull requests (PRs). To create a new PR, fork the project in GitHub and clone the upstream repo: ```sh go get -d go.opentelemetry.io/otel ``` (This may print some warning about "build constraints exclude all Go files", just ignore it.) This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You can alternatively use `git` directly with: ```sh git clone https://github.com/open-telemetry/opentelemetry-go ``` (Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name - that name is a kind of a redirector to GitHub that `go get` can understand, but `git` does not.) This would put the project in the `opentelemetry-go` directory in current working directory. Enter the newly created directory and add your fork as a new remote: ```sh git remote add git@github.com:/opentelemetry-go ``` Check out a new branch, make modifications, run linters and tests, update `CHANGELOG.md`, and push the branch to your fork: ```sh git checkout -b # edit files # update changelog make precommit git add -p git commit git push ``` Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull request ID to the entry you added to `CHANGELOG.md`. ### How to Receive Comments * If the PR is not ready for review, please put `[WIP]` in the title, tag it as `work-in-progress`, or mark it as [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). * Make sure CLA is signed and CI is clear. ### How to Get PRs Merged A PR is considered to be **ready to merge** when: * It has received two approvals from Collaborators/Maintainers (at different companies). This is not enforced through technical means and a PR may be **ready to merge** with a single approval if the change and its approach have been discussed and consensus reached. * Feedback has been addressed. * Any substantive changes to your PR will require that you clear any prior Approval reviews, this includes changes resulting from other feedback. Unless the approver explicitly stated that their approval will persist across changes it should be assumed that the PR needs their review again. Other project members (e.g. approvers, maintainers) can help with this if there are any questions or if you forget to clear reviews. * It has been open for review for at least one working day. This gives people reasonable time to review. * Trivial changes (typo, cosmetic, doc, etc.) do not have to wait for one day and may be merged with a single Maintainer's approval. * `CHANGELOG.md` has been updated to reflect what has been added, changed, removed, or fixed. * `README.md` has been updated if necessary. * Urgent fix can take exception as long as it has been actively communicated. Any Maintainer can merge the PR once it is **ready to merge**. ## Design Choices As with other OpenTelemetry clients, opentelemetry-go follows the [opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification). It's especially valuable to read through the [library guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/library-guidelines.md). ### Focus on Capabilities, Not Structure Compliance OpenTelemetry is an evolving specification, one where the desires and use cases are clear, but the method to satisfy those uses cases are not. As such, Contributions should provide functionality and behavior that conforms to the specification, but the interface and structure is flexible. It is preferable to have contributions follow the idioms of the language rather than conform to specific API names or argument patterns in the spec. For a deeper discussion, see [this](https://github.com/open-telemetry/opentelemetry-specification/issues/165). ## Documentation Each non-example Go Module should have its own `README.md` containing: - A pkg.go.dev badge which can be generated [here](https://pkg.go.dev/badge/). - Brief description. - Installation instructions (and requirements if applicable). - Hyperlink to an example. Depending on the component the example can be: - An `example_test.go` like [here](exporters/stdout/stdouttrace/example_test.go). - A sample Go application with its own `README.md`, like [here](example/zipkin). - Additional documentation sections such us: - Configuration, - Contributing, - References. [Here](exporters/jaeger/README.md) is an example of a concise `README.md`. Moreover, it should be possible to navigate to any `README.md` from the root `README.md`. ## Style Guide One of the primary goals of this project is that it is actually used by developers. With this goal in mind the project strives to build user-friendly and idiomatic Go code adhering to the Go community's best practices. For a non-comprehensive but foundational overview of these best practices the [Effective Go](https://golang.org/doc/effective_go.html) documentation is an excellent starting place. As a convenience for developers building this project the `make precommit` will format, lint, validate, and in some cases fix the changes you plan to submit. This check will need to pass for your changes to be able to be merged. In addition to idiomatic Go, the project has adopted certain standards for implementations of common patterns. These standards should be followed as a default, and if they are not followed documentation needs to be included as to the reasons why. ### Configuration When creating an instantiation function for a complex `type T struct`, it is useful to allow variable number of options to be applied. However, the strong type system of Go restricts the function design options. There are a few ways to solve this problem, but we have landed on the following design. #### `config` Configuration should be held in a `struct` named `config`, or prefixed with specific type name this Configuration applies to if there are multiple `config` in the package. This type must contain configuration options. ```go // config contains configuration options for a thing. type config struct { // options ... } ``` In general the `config` type will not need to be used externally to the package and should be unexported. If, however, it is expected that the user will likely want to build custom options for the configuration, the `config` should be exported. Please, include in the documentation for the `config` how the user can extend the configuration. It is important that internal `config` are not shared across package boundaries. Meaning a `config` from one package should not be directly used by another. The one exception is the API packages. The configs from the base API, eg. `go.opentelemetry.io/otel/trace.TracerConfig` and `go.opentelemetry.io/otel/metric.InstrumentConfig`, are intended to be consumed by the SDK therefor it is expected that these are exported. When a config is exported we want to maintain forward and backward compatibility, to achieve this no fields should be exported but should instead be accessed by methods. Optionally, it is common to include a `newConfig` function (with the same naming scheme). This function wraps any defaults setting and looping over all options to create a configured `config`. ```go // newConfig returns an appropriately configured config. func newConfig([]Option) config { // Set default values for config. config := config{/* […] */} for _, option := range options { option.apply(&config) } // Preform any validation here. return config } ``` If validation of the `config` options is also preformed this can return an error as well that is expected to be handled by the instantiation function or propagated to the user. Given the design goal of not having the user need to work with the `config`, the `newConfig` function should also be unexported. #### `Option` To set the value of the options a `config` contains, a corresponding `Option` interface type should be used. ```go type Option interface { apply(*config) } ``` Having `apply` unexported makes sure that it will not be used externally. Moreover, the interface becomes sealed so the user cannot easily implement the interface on its own. The name of the interface should be prefixed in the same way the corresponding `config` is (if at all). #### Options All user configurable options for a `config` must have a related unexported implementation of the `Option` interface and an exported configuration function that wraps this implementation. The wrapping function name should be prefixed with `With*` (or in the special case of a boolean options `Without*`) and should have the following function signature. ```go func With*(…) Option { … } ``` ##### `bool` Options ```go type defaultFalseOption bool func (o defaultFalseOption) apply(c *config) { c.Bool = bool(o) } // WithOption sets a T to have an option included. func WithOption() Option { return defaultFalseOption(true) } ``` ```go type defaultTrueOption bool func (o defaultTrueOption) apply(c *config) { c.Bool = bool(o) } // WithoutOption sets a T to have Bool option excluded. func WithoutOption() Option { return defaultTrueOption(false) } ``` ##### Declared Type Options ```go type myTypeOption struct { MyType MyType } func (o myTypeOption) apply(c *config) { c.MyType = o.MyType } // WithMyType sets T to have include MyType. func WithMyType(t MyType) Option { return myTypeOption{t} } ``` ##### Functional Options ```go type optionFunc func(*config) func (fn optionFunc) apply(c *config) { fn(c) } // WithMyType sets t as MyType. func WithMyType(t MyType) Option { return optionFunc(func(c *config) { c.MyType = t }) } ``` #### Instantiation Using this configuration pattern to configure instantiation with a `NewT` function. ```go func NewT(options ...Option) T {…} ``` Any required parameters can be declared before the variadic `options`. #### Dealing with Overlap Sometimes there are multiple complex `struct` that share common configuration and also have distinct configuration. To avoid repeated portions of `config`s, a common `config` can be used with the union of options being handled with the `Option` interface. For example. ```go // config holds options for all animals. type config struct { Weight float64 Color string MaxAltitude float64 } // DogOption apply Dog specific options. type DogOption interface { applyDog(*config) } // BirdOption apply Bird specific options. type BirdOption interface { applyBird(*config) } // Option apply options for all animals. type Option interface { BirdOption DogOption } type weightOption float64 func (o weightOption) applyDog(c *config) { c.Weight = float64(o) } func (o weightOption) applyBird(c *config) { c.Weight = float64(o) } func WithWeight(w float64) Option { return weightOption(w) } type furColorOption string func (o furColorOption) applyDog(c *config) { c.Color = string(o) } func WithFurColor(c string) DogOption { return furColorOption(c) } type maxAltitudeOption float64 func (o maxAltitudeOption) applyBird(c *config) { c.MaxAltitude = float64(o) } func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } func NewDog(name string, o ...DogOption) Dog {…} func NewBird(name string, o ...BirdOption) Bird {…} ``` ### Interfaces To allow other developers to better comprehend the code, it is important to ensure it is sufficiently documented. One simple measure that contributes to this aim is self-documenting by naming method parameters. Therefore, where appropriate, methods of every exported interface type should have their parameters appropriately named. #### Interface Stability All exported stable interfaces that include the following warning in their doumentation are allowed to be extended with additional methods. > Warning: methods may be added to this interface in minor releases. Otherwise, stable interfaces MUST NOT be modified. If new functionality is needed for an interface that cannot be changed it MUST be added by including an additional interface. That added interface can be a simple interface for the specific functionality that you want to add or it can be a super-set of the original interface. For example, if you wanted to a `Close` method to the `Exporter` interface: ```go type Exporter interface { Export() } ``` A new interface, `Closer`, can be added: ```go type Closer interface { Close() } ``` Code that is passed the `Exporter` interface can now check to see if the passed value also satisfies the new interface. E.g. ```go func caller(e Exporter) { /* ... */ if c, ok := e.(Closer); ok { c.Close() } /* ... */ } ``` Alternatively, a new type that is the super-set of an `Exporter` can be created. ```go type ClosingExporter struct { Exporter Close() } ``` This new type can be used similar to the simple interface above in that a passed `Exporter` type can be asserted to satisfy the `ClosingExporter` type and the `Close` method called. This super-set approach can be useful if there is explicit behavior that needs to be coupled with the original type and passed as a unified type to a new function, but, because of this coupling, it also limits the applicability of the added functionality. If there exist other interfaces where this functionality should be added, each one will need their own super-set interfaces and will duplicate the pattern. For this reason, the simple targeted interface that defines the specific functionality should be preferred. ## Approvers and Maintainers Approvers: - [Evan Torrie](https://github.com/evantorrie), Verizon Media - [Josh MacDonald](https://github.com/jmacd), LightStep - [Sam Xie](https://github.com/XSAM) - [David Ashpole](https://github.com/dashpole), Google - [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep - [Aaron Clawson](https://github.com/MadVikingGod) - [Robert Pająk](https://github.com/pellared), Splunk Maintainers: - [Anthony Mirabella](https://github.com/Aneurysm9), AWS - [Tyler Yahn](https://github.com/MrAlias), Splunk ### Become an Approver or a Maintainer See the [community membership document in OpenTelemetry community repo](https://github.com/open-telemetry/community/blob/main/community-membership.md). golang-opentelemetry-otel-1.1.0/LICENSE000066400000000000000000000261351414226744000176330ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. golang-opentelemetry-otel-1.1.0/Makefile000066400000000000000000000147251414226744000202700ustar00rootroot00000000000000# 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. EXAMPLES := $(shell ./get_main_pkgs.sh ./example) TOOLS_MOD_DIR := ./internal/tools # All source code and documents. Used in spell check. ALL_DOCS := $(shell find . -name '*.md' -type f | sort) # All directories with go.mod files related to opentelemetry library. Used for building, testing and linting. ALL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example' | sort)) $(shell find ./example -type f -name 'go.mod' -exec dirname {} \; | sort) ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort) GO = go TIMEOUT = 60 .DEFAULT_GOAL := precommit .PHONY: precommit ci precommit: dependabot-check license-check lint build examples test-default ci: precommit check-clean-work-tree test-coverage # Tools TOOLS = $(CURDIR)/.tools $(TOOLS): @mkdir -p $@ $(TOOLS)/%: | $(TOOLS) cd $(TOOLS_MOD_DIR) && \ $(GO) build -o $@ $(PACKAGE) MULTIMOD = $(TOOLS)/multimod $(TOOLS)/multimod: PACKAGE=go.opentelemetry.io/build-tools/multimod SEMCONVGEN = $(TOOLS)/semconvgen $(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen CROSSLINK = $(TOOLS)/crosslink $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink GOLANGCI_LINT = $(TOOLS)/golangci-lint $(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint MISSPELL = $(TOOLS)/misspell $(TOOLS)/misspell: PACKAGE=github.com/client9/misspell/cmd/misspell GOCOVMERGE = $(TOOLS)/gocovmerge $(TOOLS)/gocovmerge: PACKAGE=github.com/wadey/gocovmerge STRINGER = $(TOOLS)/stringer $(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer PORTO = $(TOOLS)/porto $(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto GOJQ = $(TOOLS)/gojq $(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq .PHONY: tools tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) # Build .PHONY: examples generate build examples: @set -e; for dir in $(EXAMPLES); do \ echo "$(GO) build $${dir}/..."; \ (cd "$${dir}" && \ $(GO) build .); \ done generate: $(STRINGER) set -e; for dir in $(ALL_GO_MOD_DIRS); do \ echo "$(GO) generate $${dir}/..."; \ (cd "$${dir}" && \ PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && \ $(PORTO) -w .); \ done build: generate # Build all package code including testing code. set -e; for dir in $(ALL_GO_MOD_DIRS); do \ echo "$(GO) build $${dir}/..."; \ (cd "$${dir}" && \ $(GO) build ./... && \ $(GO) list ./... \ | grep -v third_party \ | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null); \ done # Tests TEST_TARGETS := test-default test-bench test-short test-verbose test-race .PHONY: $(TEST_TARGETS) test test-default: ARGS=-v -race test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. test-short: ARGS=-short test-verbose: ARGS=-v test-race: ARGS=-race $(TEST_TARGETS): test test: @set -e; for dir in $(ALL_GO_MOD_DIRS); do \ echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $${dir}/..."; \ (cd "$${dir}" && \ $(GO) list ./... \ | grep -v third_party \ | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS)); \ done COVERAGE_MODE = atomic COVERAGE_PROFILE = coverage.out .PHONY: test-coverage test-coverage: | $(GOCOVMERGE) @set -e; \ printf "" > coverage.txt; \ for dir in $(ALL_COVERAGE_MOD_DIRS); do \ echo "$(GO) test -coverpkg=go.opentelemetry.io/otel/... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" $${dir}/..."; \ (cd "$${dir}" && \ $(GO) list ./... \ | grep -v third_party \ | xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \ $(GO) tool cover -html=coverage.out -o coverage.html); \ done; \ $(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt .PHONY: lint lint: misspell lint-modules | $(GOLANGCI_LINT) set -e; for dir in $(ALL_GO_MOD_DIRS); do \ echo "golangci-lint in $${dir}"; \ (cd "$${dir}" && \ $(GOLANGCI_LINT) run --fix && \ $(GOLANGCI_LINT) run); \ done .PHONY: vanity-import-check vanity-import-check: | $(PORTO) $(PORTO) -l . .PHONY: misspell misspell: | $(MISSPELL) $(MISSPELL) -w $(ALL_DOCS) .PHONY: lint-modules lint-modules: | $(CROSSLINK) set -e; for dir in $(ALL_GO_MOD_DIRS) $(TOOLS_MOD_DIR); do \ echo "$(GO) mod tidy in $${dir}"; \ (cd "$${dir}" && \ $(GO) mod tidy); \ done echo "cross-linking all go modules" $(CROSSLINK) .PHONY: license-check license-check: @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; do \ awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi .PHONY: dependabot-check dependabot-check: @result=$$( \ for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.//' ); \ do grep -q "directory: \+$$f" .github/dependabot.yml \ || echo "$$f"; \ done; \ ); \ if [ -n "$$result" ]; then \ echo "missing go.mod dependabot check:"; echo "$$result"; \ echo "new modules need to be added to the .github/dependabot.yml file"; \ exit 1; \ fi .PHONY: check-clean-work-tree check-clean-work-tree: @if ! git diff --quiet; then \ echo; \ echo 'Working tree is not clean, did you forget to run "make precommit"?'; \ echo; \ git status; \ exit 1; \ fi .PHONY: prerelease prerelease: | $(MULTIMOD) @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET} COMMIT ?= "HEAD" .PHONY: add-tags add-tags: | $(MULTIMOD) @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT} golang-opentelemetry-otel-1.1.0/README.md000066400000000000000000000112111414226744000200720ustar00rootroot00000000000000# OpenTelemetry-Go [![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain) [![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-go/coverage.svg?branch=main)](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main) [![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel) [![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel) [![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT) OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/). It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms. ## Project Status | Signal | Status | Project | | ------- | ---------- | ------- | | Traces | Stable | N/A | | Metrics | Alpha | N/A | | Logs | Frozen [1] | N/A | - [1]: The Logs signal development is halted for this project while we develop both Traces and Metrics. No Logs Pull Requests are currently being accepted. Progress and status specific to this repository is tracked in our local [project boards](https://github.com/open-telemetry/opentelemetry-go/projects) and [milestones](https://github.com/open-telemetry/opentelemetry-go/milestones). Project versioning information and stability guarantees can be found in the [versioning documentation](./VERSIONING.md). ### Compatibility This project is tested on the following systems. | OS | Go Version | Architecture | | ------- | ---------- | ------------ | | Ubuntu | 1.17 | amd64 | | Ubuntu | 1.16 | amd64 | | Ubuntu | 1.15 | amd64 | | Ubuntu | 1.17 | 386 | | Ubuntu | 1.16 | 386 | | Ubuntu | 1.15 | 386 | | MacOS | 1.17 | amd64 | | MacOS | 1.16 | amd64 | | MacOS | 1.15 | amd64 | | Windows | 1.17 | amd64 | | Windows | 1.16 | amd64 | | Windows | 1.15 | amd64 | | Windows | 1.17 | 386 | | Windows | 1.16 | 386 | | Windows | 1.15 | 386 | While this project should work for other systems, no compatibility guarantees are made for those systems currently. ## Getting Started You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/). OpenTelemetry's goal is to provide a single set of APIs to capture distributed traces and metrics from your application and send them to an observability platform. This project allows you to do just that for applications written in Go. There are two steps to this process: instrument your application, and configure an exporter. ### Instrumentation To start capturing distributed traces and metric events from your application it first needs to be instrumented. The easiest way to do this is by using an instrumentation library for your code. Be sure to check out [the officially supported instrumentation libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation). If you need to extend the telemetry an instrumentation library provides or want to build your own instrumentation for your application directly you will need to use the [go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api) package. The included [examples](./example/) are a good way to see some practical uses of this process. ### Export Now that your application is instrumented to collect telemetry, it needs an export pipeline to send that telemetry to an observability platform. All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters). | Exporter | Metrics | Traces | | :-----------------------------------: | :-----: | :----: | | [Jaeger](./exporters/jaeger/) | | ✓ | | [OTLP](./exporters/otlp/) | ✓ | ✓ | | [Prometheus](./exporters/prometheus/) | ✓ | | | [stdout](./exporters/stdout/) | ✓ | ✓ | | [Zipkin](./exporters/zipkin/) | | ✓ | Additionally, OpenTelemetry community supported exporters can be found in the [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/exporters). ## Contributing See the [contributing documentation](CONTRIBUTING.md). golang-opentelemetry-otel-1.1.0/RELEASING.md000066400000000000000000000130461414226744000204560ustar00rootroot00000000000000# Release Process ## Semantic Convention Generation If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility installed by `make tools` that can be used to generate the a package with the name matching the specification version number under the `semconv` package. This will ideally be done soon after the specification release is tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the generated files match the released semantic conventions. There are currently two categories of semantic conventions that must be generated, `resource` and `trace`. ``` .tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2 .tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2 ``` Using default values for all options other than `input` will result in using the `template.j2` template to generate `resource.go` and `trace.go` in `/path/to/otelgo/repo/semconv/`. There are several ancillary files that are not generated and should be copied into the new package from the prior package, with updates made as appropriate to canonical import path statements and constant values. These files include: * doc.go * exception.go * http(_test)?.go * schema.go Uses of the previous schema version in this repository should be updated to use the newly generated version. No tooling for this exists at present, so use find/replace in your editor of choice or craft a `grep | sed` pipeline if you like living on the edge. ## Pre-Release First, decide which module sets will be released and update their versions in `versions.yaml`. Commit this change to a new branch. Update go.mod for submodules to depend on the new release which will happen in the next step. 1. Run the `prerelease` make target. It creates a branch `prerelease__` that will contain all release changes. ``` make prerelease MODSET= ``` 2. Verify the changes. ``` git diff ...prerelease__ ``` This should have changed the version for all modules to be ``. If these changes look correct, merge them into your pre-release branch: ```go git merge prerelease__ ``` 3. Update the [Changelog](./CHANGELOG.md). - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. To verify this, you can look directly at the commits since the ``. ``` git --no-pager log --pretty=oneline "..HEAD" ``` - Move all the `Unreleased` changes into a new section following the title scheme (`[] - `). - Update all the appropriate links at the bottom. 4. Push the changes to upstream and create a Pull Request on GitHub. Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description. ## Tag Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit. ***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step! Failure to do so will leave things in a broken state. As long as you do not change `versions.yaml` between pre-release and this step, things should be fine. ***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189). It is critical you make sure the version you push upstream is correct. [Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331). 1. For each module set that will be released, run the `add-tags` make target using the `` of the commit on the main branch for the merged Pull Request. ``` make add-tags MODSET= COMMIT= ``` It should only be necessary to provide an explicit `COMMIT` value if the current `HEAD` of your working directory is not the correct commit. 2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`). Make sure you push all sub-modules as well. ``` git push upstream git push upstream ... ``` ## Release Finally create a Release for the new `` on GitHub. The release body should include all the release notes from the Changelog for this release. Additionally, the `tag.sh` script generates commit logs since last release which can be used to supplement the release notes. ## Verify Examples After releasing verify that examples build outside of the repository. ``` ./verify_examples.sh ``` The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them. This ensures they build with the published release, not the local copy. ## Post-Release ### Contrib Repository Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md) that uses this release. ### Website Documentation Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/). Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate. golang-opentelemetry-otel-1.1.0/VERSIONING.md000066400000000000000000000253541414226744000206350ustar00rootroot00000000000000# Versioning This document describes the versioning policy for this repository. This policy is designed so the following goals can be achieved. **Users are provided a codebase of value that is stable and secure.** ## Policy * Versioning of this project will be idiomatic of a Go project using [Go modules](https://github.com/golang/go/wiki/Modules). * [Semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) will be used. * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html) with the following exceptions. * New methods may be added to exported API interfaces. All exported interfaces that fall within this exception will include the following paragraph in their public documentation. > Warning: methods may be added to this interface in minor releases. * If a module is version `v2` or higher, the major version of the module must be included as a `/vN` at the end of the module paths used in `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the paths used in `go get` commands (e.g., `go get go.opentelemetry.io/otel/v2@v2.0.1`. Note there is both a `/v2` and a `@v2.0.1` in that example. One way to think about it is that the module name now includes the `/v2`, so include `/v2` whenever you are using the module name). * If a module is version `v0` or `v1`, do not include the major version in either the module path or the import path. * Modules will be used to encapsulate signals and components. * Experimental modules still under active development will be versioned at `v0` to imply the stability guarantee defined by [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). > Major version zero (0.y.z) is for initial development. Anything MAY > change at any time. The public API SHOULD NOT be considered stable. * Mature modules for which we guarantee a stable public API will be versioned with a major version greater than `v0`. * The decision to make a module stable will be made on a case-by-case basis by the maintainers of this project. * Experimental modules will start their versioning at `v0.0.0` and will increment their minor version when backwards incompatible changes are released and increment their patch version when backwards compatible changes are released. * All stable modules that use the same major version number will use the same entire version number. * Stable modules may be released with an incremented minor or patch version even though that module has not been changed, but rather so that it will remain at the same version as other stable modules that did undergo change. * When an experimental module becomes stable a new stable module version will be released and will include this now stable module. The new stable module version will be an increment of the minor version number and will be applied to all existing stable modules as well as the newly stable module being released. * Versioning of the associated [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of this project will be idiomatic of a Go project using [Go modules](https://github.com/golang/go/wiki/Modules). * [Semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) will be used. * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). * If a module is version `v2` or higher, the major version of the module must be included as a `/vN` at the end of the module paths used in `go.mod` files (e.g., `module go.opentelemetry.io/contrib/instrumentation/host/v2`, `require go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the package import path (e.g., `import "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes the paths used in `go get` commands (e.g., `go get go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`. Note there is both a `/v2` and a `@v2.0.1` in that example. One way to think about it is that the module name now includes the `/v2`, so include `/v2` whenever you are using the module name). * If a module is version `v0` or `v1`, do not include the major version in either the module path or the import path. * In addition to public APIs, telemetry produced by stable instrumentation will remain stable and backwards compatible. This is to avoid breaking alerts and dashboard. * Modules will be used to encapsulate instrumentation, detectors, exporters, propagators, and any other independent sets of related components. * Experimental modules still under active development will be versioned at `v0` to imply the stability guarantee defined by [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). > Major version zero (0.y.z) is for initial development. Anything MAY > change at any time. The public API SHOULD NOT be considered stable. * Mature modules for which we guarantee a stable public API and telemetry will be versioned with a major version greater than `v0`. * Experimental modules will start their versioning at `v0.0.0` and will increment their minor version when backwards incompatible changes are released and increment their patch version when backwards compatible changes are released. * Stable contrib modules cannot depend on experimental modules from this project. * All stable contrib modules of the same major version with this project will use the same entire version as this project. * Stable modules may be released with an incremented minor or patch version even though that module's code has not been changed. Instead the only change that will have been included is to have updated that modules dependency on this project's stable APIs. * When an experimental module in contrib becomes stable a new stable module version will be released and will include this now stable module. The new stable module version will be an increment of the minor version number and will be applied to all existing stable contrib modules, this project's modules, and the newly stable module being released. * Contrib modules will be kept up to date with this project's releases. * Due to the dependency contrib modules will implicitly have on this project's modules the release of stable contrib modules to match the released version number will be staggered after this project's release. There is no explicit time guarantee for how long after this projects release the contrib release will be. Effort should be made to keep them as close in time as possible. * No additional stable release in this project can be made until the contrib repository has a matching stable release. * No release can be made in the contrib repository after this project's stable release except for a stable release of the contrib repository. * GitHub releases will be made for all releases. * Go modules will be made available at Go package mirrors. ## Example Versioning Lifecycle To better understand the implementation of the above policy the following example is provided. This project is simplified to include only the following modules and their versions: * `otel`: `v0.14.0` * `otel/trace`: `v0.14.0` * `otel/metric`: `v0.14.0` * `otel/baggage`: `v0.14.0` * `otel/sdk/trace`: `v0.14.0` * `otel/sdk/metric`: `v0.14.0` These modules have been developed to a point where the `otel/trace`, `otel/baggage`, and `otel/sdk/trace` modules have reached a point that they should be considered for a stable release. The `otel/metric` and `otel/sdk/metric` are still under active development and the `otel` module depends on both `otel/trace` and `otel/metric`. The `otel` package is refactored to remove its dependencies on `otel/metric` so it can be released as stable as well. With that done the following release candidates are made: * `otel`: `v1.0.0-RC1` * `otel/trace`: `v1.0.0-RC1` * `otel/baggage`: `v1.0.0-RC1` * `otel/sdk/trace`: `v1.0.0-RC1` The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`. A few minor issues are discovered in the `otel/trace` package. These issues are resolved with some minor, but backwards incompatible, changes and are released as a second release candidate: * `otel`: `v1.0.0-RC2` * `otel/trace`: `v1.0.0-RC2` * `otel/baggage`: `v1.0.0-RC2` * `otel/sdk/trace`: `v1.0.0-RC2` Notice that all module version numbers are incremented to adhere to our versioning policy. After these release candidates have been evaluated to satisfaction, they are released as version `v1.0.0`. * `otel`: `v1.0.0` * `otel/trace`: `v1.0.0` * `otel/baggage`: `v1.0.0` * `otel/sdk/trace`: `v1.0.0` Since both the `go` utility and the Go module system support [the semantic versioning definition of precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release will correctly be interpreted as the successor to the previous release candidates. Active development of this project continues. The `otel/metric` module now has backwards incompatible changes to its API that need to be released and the `otel/baggage` module has a minor bug fix that needs to be released. The following release is made: * `otel`: `v1.0.1` * `otel/trace`: `v1.0.1` * `otel/metric`: `v0.15.0` * `otel/baggage`: `v1.0.1` * `otel/sdk/trace`: `v1.0.1` * `otel/sdk/metric`: `v0.15.0` Notice that, again, all stable module versions are incremented in unison and the `otel/sdk/metric` package, which depends on the `otel/metric` package, also bumped its version. This bump of the `otel/sdk/metric` package makes sense given their coupling, though it is not explicitly required by our versioning policy. As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a point where they should be evaluated for stability. The `otel` module is reintegrated with the `otel/metric` package and the following release is made: * `otel`: `v1.1.0-RC1` * `otel/trace`: `v1.1.0-RC1` * `otel/metric`: `v1.1.0-RC1` * `otel/baggage`: `v1.1.0-RC1` * `otel/sdk/trace`: `v1.1.0-RC1` * `otel/sdk/metric`: `v1.1.0-RC1` All the modules are evaluated and determined to a viable stable release. They are then released as version `v1.1.0` (the minor version is incremented to indicate the addition of new signal). * `otel`: `v1.1.0` * `otel/trace`: `v1.1.0` * `otel/metric`: `v1.1.0` * `otel/baggage`: `v1.1.0` * `otel/sdk/trace`: `v1.1.0` * `otel/sdk/metric`: `v1.1.0` golang-opentelemetry-otel-1.1.0/attribute/000077500000000000000000000000001414226744000206225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/attribute/benchmark_test.go000066400000000000000000000143451414226744000241510ustar00rootroot00000000000000// 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 attribute_test import ( "testing" "go.opentelemetry.io/otel/attribute" ) // Store results in a file scope var to ensure compiler does not optimize the // test away. var ( outV attribute.Value outKV attribute.KeyValue outBool bool outBoolSlice []bool outInt64 int64 outInt64Slice []int64 outFloat64 float64 outFloat64Slice []float64 outStr string outStrSlice []string ) func benchmarkEmit(kv attribute.KeyValue) func(*testing.B) { return func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outStr = kv.Value.Emit() } } } func BenchmarkBool(b *testing.B) { k, v := "bool", true kv := attribute.Bool(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.BoolValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Bool(k, v) } }) b.Run("AsBool", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outBool = kv.Value.AsBool() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkBoolSlice(b *testing.B) { k, v := "bool slice", []bool{true, false, true} kv := attribute.BoolSlice(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.BoolSliceValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.BoolSlice(k, v) } }) b.Run("AsBoolSlice", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outBoolSlice = kv.Value.AsBoolSlice() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkInt(b *testing.B) { k, v := "int", int(42) kv := attribute.Int(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.IntValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Int(k, v) } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkIntSlice(b *testing.B) { k, v := "int slice", []int{42, -3, 12} kv := attribute.IntSlice(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.IntSliceValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.IntSlice(k, v) } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkInt64(b *testing.B) { k, v := "int64", int64(42) kv := attribute.Int64(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.Int64Value(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Int64(k, v) } }) b.Run("AsInt64", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outInt64 = kv.Value.AsInt64() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkInt64Slice(b *testing.B) { k, v := "int64 slice", []int64{42, -3, 12} kv := attribute.Int64Slice(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.Int64SliceValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Int64Slice(k, v) } }) b.Run("AsInt64Slice", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outInt64Slice = kv.Value.AsInt64Slice() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkFloat64(b *testing.B) { k, v := "float64", float64(42) kv := attribute.Float64(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.Float64Value(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Float64(k, v) } }) b.Run("AsFloat64", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outFloat64 = kv.Value.AsFloat64() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkFloat64Slice(b *testing.B) { k, v := "float64 slice", []float64{42, -3, 12} kv := attribute.Float64Slice(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.Float64SliceValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.Float64Slice(k, v) } }) b.Run("AsFloat64Slice", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outFloat64Slice = kv.Value.AsFloat64Slice() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkString(b *testing.B) { k, v := "string", "42" kv := attribute.String(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.StringValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.String(k, v) } }) b.Run("AsString", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outStr = kv.Value.AsString() } }) b.Run("Emit", benchmarkEmit(kv)) } func BenchmarkStringSlice(b *testing.B) { k, v := "float64 slice", []string{"forty-two", "negative three", "twelve"} kv := attribute.StringSlice(k, v) b.Run("Value", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outV = attribute.StringSliceValue(v) } }) b.Run("KeyValue", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outKV = attribute.StringSlice(k, v) } }) b.Run("AsStringSlice", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { outStrSlice = kv.Value.AsStringSlice() } }) b.Run("Emit", benchmarkEmit(kv)) } golang-opentelemetry-otel-1.1.0/attribute/doc.go000066400000000000000000000013171414226744000217200ustar00rootroot00000000000000// 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 attribute provides key and value attributes. package attribute // import "go.opentelemetry.io/otel/attribute" golang-opentelemetry-otel-1.1.0/attribute/encoder.go000066400000000000000000000107071414226744000225750ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" import ( "bytes" "sync" "sync/atomic" ) type ( // Encoder is a mechanism for serializing a label set into a // specific string representation that supports caching, to // avoid repeated serialization. An example could be an // exporter encoding the label set into a wire representation. Encoder interface { // Encode returns the serialized encoding of the label // set using its Iterator. This result may be cached // by a attribute.Set. Encode(iterator Iterator) string // ID returns a value that is unique for each class of // label encoder. Label encoders allocate these using // `NewEncoderID`. ID() EncoderID } // EncoderID is used to identify distinct Encoder // implementations, for caching encoded results. EncoderID struct { value uint64 } // defaultLabelEncoder uses a sync.Pool of buffers to reduce // the number of allocations used in encoding labels. This // implementation encodes a comma-separated list of key=value, // with '/'-escaping of '=', ',', and '\'. defaultLabelEncoder struct { // pool is a pool of labelset builders. The buffers in this // pool grow to a size that most label encodings will not // allocate new memory. pool sync.Pool // *bytes.Buffer } ) // escapeChar is used to ensure uniqueness of the label encoding where // keys or values contain either '=' or ','. Since there is no parser // needed for this encoding and its only requirement is to be unique, // this choice is arbitrary. Users will see these in some exporters // (e.g., stdout), so the backslash ('\') is used as a conventional choice. const escapeChar = '\\' var ( _ Encoder = &defaultLabelEncoder{} // encoderIDCounter is for generating IDs for other label // encoders. encoderIDCounter uint64 defaultEncoderOnce sync.Once defaultEncoderID = NewEncoderID() defaultEncoderInstance *defaultLabelEncoder ) // NewEncoderID returns a unique label encoder ID. It should be // called once per each type of label encoder. Preferably in init() or // in var definition. func NewEncoderID() EncoderID { return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)} } // DefaultEncoder returns a label encoder that encodes labels // in such a way that each escaped label's key is followed by an equal // sign and then by an escaped label's value. All key-value pairs are // separated by a comma. // // Escaping is done by prepending a backslash before either a // backslash, equal sign or a comma. func DefaultEncoder() Encoder { defaultEncoderOnce.Do(func() { defaultEncoderInstance = &defaultLabelEncoder{ pool: sync.Pool{ New: func() interface{} { return &bytes.Buffer{} }, }, } }) return defaultEncoderInstance } // Encode is a part of an implementation of the LabelEncoder // interface. func (d *defaultLabelEncoder) Encode(iter Iterator) string { buf := d.pool.Get().(*bytes.Buffer) defer d.pool.Put(buf) buf.Reset() for iter.Next() { i, keyValue := iter.IndexedLabel() if i > 0 { _, _ = buf.WriteRune(',') } copyAndEscape(buf, string(keyValue.Key)) _, _ = buf.WriteRune('=') if keyValue.Value.Type() == STRING { copyAndEscape(buf, keyValue.Value.AsString()) } else { _, _ = buf.WriteString(keyValue.Value.Emit()) } } return buf.String() } // ID is a part of an implementation of the LabelEncoder interface. func (*defaultLabelEncoder) ID() EncoderID { return defaultEncoderID } // copyAndEscape escapes `=`, `,` and its own escape character (`\`), // making the default encoding unique. func copyAndEscape(buf *bytes.Buffer, val string) { for _, ch := range val { switch ch { case '=', ',', escapeChar: buf.WriteRune(escapeChar) } buf.WriteRune(ch) } } // Valid returns true if this encoder ID was allocated by // `NewEncoderID`. Invalid encoder IDs will not be cached. func (id EncoderID) Valid() bool { return id.value != 0 } golang-opentelemetry-otel-1.1.0/attribute/iterator.go000066400000000000000000000066301414226744000230070ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" // Iterator allows iterating over the set of labels in order, // sorted by key. type Iterator struct { storage *Set idx int } // MergeIterator supports iterating over two sets of labels while // eliminating duplicate values from the combined set. The first // iterator value takes precedence. type MergeIterator struct { one oneIterator two oneIterator current KeyValue } type oneIterator struct { iter Iterator done bool label KeyValue } // Next moves the iterator to the next position. Returns false if there // are no more labels. func (i *Iterator) Next() bool { i.idx++ return i.idx < i.Len() } // Label returns current KeyValue. Must be called only after Next returns // true. func (i *Iterator) Label() KeyValue { kv, _ := i.storage.Get(i.idx) return kv } // Attribute is a synonym for Label(). func (i *Iterator) Attribute() KeyValue { return i.Label() } // IndexedLabel returns current index and attribute. Must be called only // after Next returns true. func (i *Iterator) IndexedLabel() (int, KeyValue) { return i.idx, i.Label() } // Len returns a number of labels in the iterator's `*Set`. func (i *Iterator) Len() int { return i.storage.Len() } // ToSlice is a convenience function that creates a slice of labels // from the passed iterator. The iterator is set up to start from the // beginning before creating the slice. func (i *Iterator) ToSlice() []KeyValue { l := i.Len() if l == 0 { return nil } i.idx = -1 slice := make([]KeyValue, 0, l) for i.Next() { slice = append(slice, i.Label()) } return slice } // NewMergeIterator returns a MergeIterator for merging two label sets // Duplicates are resolved by taking the value from the first set. func NewMergeIterator(s1, s2 *Set) MergeIterator { mi := MergeIterator{ one: makeOne(s1.Iter()), two: makeOne(s2.Iter()), } return mi } func makeOne(iter Iterator) oneIterator { oi := oneIterator{ iter: iter, } oi.advance() return oi } func (oi *oneIterator) advance() { if oi.done = !oi.iter.Next(); !oi.done { oi.label = oi.iter.Label() } } // Next returns true if there is another label available. func (m *MergeIterator) Next() bool { if m.one.done && m.two.done { return false } if m.one.done { m.current = m.two.label m.two.advance() return true } if m.two.done { m.current = m.one.label m.one.advance() return true } if m.one.label.Key == m.two.label.Key { m.current = m.one.label // first iterator label value wins m.one.advance() m.two.advance() return true } if m.one.label.Key < m.two.label.Key { m.current = m.one.label m.one.advance() return true } m.current = m.two.label m.two.advance() return true } // Label returns the current value after Next() returns true. func (m *MergeIterator) Label() KeyValue { return m.current } golang-opentelemetry-otel-1.1.0/attribute/iterator_test.go000066400000000000000000000065741414226744000240550ustar00rootroot00000000000000// 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 attribute_test import ( "fmt" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ) func TestIterator(t *testing.T) { one := attribute.String("one", "1") two := attribute.Int("two", 2) lbl := attribute.NewSet(one, two) iter := lbl.Iter() require.Equal(t, 2, iter.Len()) require.True(t, iter.Next()) require.Equal(t, one, iter.Label()) idx, attr := iter.IndexedLabel() require.Equal(t, 0, idx) require.Equal(t, one, attr) require.Equal(t, 2, iter.Len()) require.True(t, iter.Next()) require.Equal(t, two, iter.Label()) idx, attr = iter.IndexedLabel() require.Equal(t, 1, idx) require.Equal(t, two, attr) require.Equal(t, 2, iter.Len()) require.False(t, iter.Next()) require.Equal(t, 2, iter.Len()) } func TestEmptyIterator(t *testing.T) { lbl := attribute.NewSet() iter := lbl.Iter() require.Equal(t, 0, iter.Len()) require.False(t, iter.Next()) } func TestMergedIterator(t *testing.T) { type inputs struct { name string keys1 []string keys2 []string expect []string } makeLabels := func(keys []string, num int) (result []attribute.KeyValue) { for _, k := range keys { result = append(result, attribute.Int(k, num)) } return } for _, input := range []inputs{ { name: "one overlap", keys1: []string{"A", "B"}, keys2: []string{"B", "C"}, expect: []string{"A/1", "B/1", "C/2"}, }, { name: "reversed one overlap", keys1: []string{"B", "A"}, keys2: []string{"C", "B"}, expect: []string{"A/1", "B/1", "C/2"}, }, { name: "one empty", keys1: nil, keys2: []string{"C", "B"}, expect: []string{"B/2", "C/2"}, }, { name: "two empty", keys1: []string{"C", "B"}, keys2: nil, expect: []string{"B/1", "C/1"}, }, { name: "no overlap both", keys1: []string{"C"}, keys2: []string{"B"}, expect: []string{"B/2", "C/1"}, }, { name: "one empty single two", keys1: nil, keys2: []string{"B"}, expect: []string{"B/2"}, }, { name: "two empty single one", keys1: []string{"A"}, keys2: nil, expect: []string{"A/1"}, }, { name: "all empty", keys1: nil, keys2: nil, expect: nil, }, { name: "full overlap", keys1: []string{"A", "B", "C", "D"}, keys2: []string{"A", "B", "C", "D"}, expect: []string{"A/1", "B/1", "C/1", "D/1"}, }, } { t.Run(input.name, func(t *testing.T) { labels1 := makeLabels(input.keys1, 1) labels2 := makeLabels(input.keys2, 2) set1 := attribute.NewSet(labels1...) set2 := attribute.NewSet(labels2...) merge := attribute.NewMergeIterator(&set1, &set2) var result []string for merge.Next() { label := merge.Label() result = append(result, fmt.Sprint(label.Key, "/", label.Value.Emit())) } require.Equal(t, input.expect, result) }) } } golang-opentelemetry-otel-1.1.0/attribute/key.go000066400000000000000000000074761414226744000217570ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" // Key represents the key part in key-value pairs. It's a string. The // allowed character set in the key depends on the use of the key. type Key string // Bool creates a KeyValue instance with a BOOL Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Bool(name, value). func (k Key) Bool(v bool) KeyValue { return KeyValue{ Key: k, Value: BoolValue(v), } } // BoolSlice creates a KeyValue instance with a BOOLSLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- BoolSlice(name, value). func (k Key) BoolSlice(v []bool) KeyValue { return KeyValue{ Key: k, Value: BoolSliceValue(v), } } // Int creates a KeyValue instance with an INT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int(name, value). func (k Key) Int(v int) KeyValue { return KeyValue{ Key: k, Value: IntValue(v), } } // IntSlice creates a KeyValue instance with an INT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- IntSlice(name, value). func (k Key) IntSlice(v []int) KeyValue { return KeyValue{ Key: k, Value: IntSliceValue(v), } } // Int64 creates a KeyValue instance with an INT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int64(name, value). func (k Key) Int64(v int64) KeyValue { return KeyValue{ Key: k, Value: Int64Value(v), } } // Int64Slice creates a KeyValue instance with an INT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int64Slice(name, value). func (k Key) Int64Slice(v []int64) KeyValue { return KeyValue{ Key: k, Value: Int64SliceValue(v), } } // Float64 creates a KeyValue instance with a FLOAT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Float64(name, value). func (k Key) Float64(v float64) KeyValue { return KeyValue{ Key: k, Value: Float64Value(v), } } // Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Float64(name, value). func (k Key) Float64Slice(v []float64) KeyValue { return KeyValue{ Key: k, Value: Float64SliceValue(v), } } // String creates a KeyValue instance with a STRING Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- String(name, value). func (k Key) String(v string) KeyValue { return KeyValue{ Key: k, Value: StringValue(v), } } // StringSlice creates a KeyValue instance with a STRINGSLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- StringSlice(name, value). func (k Key) StringSlice(v []string) KeyValue { return KeyValue{ Key: k, Value: StringSliceValue(v), } } // Defined returns true for non-empty keys. func (k Key) Defined() bool { return len(k) != 0 } golang-opentelemetry-otel-1.1.0/attribute/key_test.go000066400000000000000000000047731414226744000230130ustar00rootroot00000000000000// 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 attribute_test import ( "encoding/json" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ) func TestDefined(t *testing.T) { for _, testcase := range []struct { name string k attribute.Key want bool }{ { name: "Key.Defined() returns true when len(v.Name) != 0", k: attribute.Key("foo"), want: true, }, { name: "Key.Defined() returns false when len(v.Name) == 0", k: attribute.Key(""), want: false, }, } { t.Run(testcase.name, func(t *testing.T) { //func (k attribute.Key) Defined() bool { have := testcase.k.Defined() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestJSONValue(t *testing.T) { var kvs interface{} = [2]attribute.KeyValue{ attribute.String("A", "B"), attribute.Int64("C", 1), } data, err := json.Marshal(kvs) require.NoError(t, err) require.Equal(t, `[{"Key":"A","Value":{"Type":"STRING","Value":"B"}},{"Key":"C","Value":{"Type":"INT64","Value":1}}]`, string(data)) } func TestEmit(t *testing.T) { for _, testcase := range []struct { name string v attribute.Value want string }{ { name: `test Key.Emit() can emit a string representing self.BOOL`, v: attribute.BoolValue(true), want: "true", }, { name: `test Key.Emit() can emit a string representing self.INT64`, v: attribute.Int64Value(42), want: "42", }, { name: `test Key.Emit() can emit a string representing self.FLOAT64`, v: attribute.Float64Value(42.1), want: "42.1", }, { name: `test Key.Emit() can emit a string representing self.STRING`, v: attribute.StringValue("foo"), want: "foo", }, } { t.Run(testcase.name, func(t *testing.T) { //proto: func (v attribute.Value) Emit() string { have := testcase.v.Emit() if have != testcase.want { t.Errorf("Want: %s, but have: %s", testcase.want, have) } }) } } golang-opentelemetry-otel-1.1.0/attribute/kv.go000066400000000000000000000046261414226744000216010ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" import ( "fmt" ) // KeyValue holds a key and value pair. type KeyValue struct { Key Key Value Value } // Valid returns if kv is a valid OpenTelemetry attribute. func (kv KeyValue) Valid() bool { return kv.Key != "" && kv.Value.Type() != INVALID } // Bool creates a KeyValue with a BOOL Value type. func Bool(k string, v bool) KeyValue { return Key(k).Bool(v) } // BoolSlice creates a KeyValue with a BOOLSLICE Value type. func BoolSlice(k string, v []bool) KeyValue { return Key(k).BoolSlice(v) } // Int creates a KeyValue with an INT64 Value type. func Int(k string, v int) KeyValue { return Key(k).Int(v) } // IntSlice creates a KeyValue with an INT64SLICE Value type. func IntSlice(k string, v []int) KeyValue { return Key(k).IntSlice(v) } // Int64 creates a KeyValue with an INT64 Value type. func Int64(k string, v int64) KeyValue { return Key(k).Int64(v) } // Int64Slice creates a KeyValue with an INT64SLICE Value type. func Int64Slice(k string, v []int64) KeyValue { return Key(k).Int64Slice(v) } // Float64 creates a KeyValue with a FLOAT64 Value type. func Float64(k string, v float64) KeyValue { return Key(k).Float64(v) } // Float64Slice creates a KeyValue with a FLOAT64SLICE Value type. func Float64Slice(k string, v []float64) KeyValue { return Key(k).Float64Slice(v) } // String creates a KeyValue with a STRING Value type. func String(k, v string) KeyValue { return Key(k).String(v) } // StringSlice creates a KeyValue with a STRINGSLICE Value type. func StringSlice(k string, v []string) KeyValue { return Key(k).StringSlice(v) } // Stringer creates a new key-value pair with a passed name and a string // value generated by the passed Stringer interface. func Stringer(k string, v fmt.Stringer) KeyValue { return Key(k).String(v.String()) } golang-opentelemetry-otel-1.1.0/attribute/kv_test.go000066400000000000000000000060351414226744000226340ustar00rootroot00000000000000// 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 attribute_test import ( "testing" "github.com/google/go-cmp/cmp" "go.opentelemetry.io/otel/attribute" ) func TestKeyValueConstructors(t *testing.T) { tt := []struct { name string actual attribute.KeyValue expected attribute.KeyValue }{ { name: "Bool", actual: attribute.Bool("k1", true), expected: attribute.KeyValue{ Key: "k1", Value: attribute.BoolValue(true), }, }, { name: "Int64", actual: attribute.Int64("k1", 123), expected: attribute.KeyValue{ Key: "k1", Value: attribute.Int64Value(123), }, }, { name: "Float64", actual: attribute.Float64("k1", 123.5), expected: attribute.KeyValue{ Key: "k1", Value: attribute.Float64Value(123.5), }, }, { name: "String", actual: attribute.String("k1", "123.5"), expected: attribute.KeyValue{ Key: "k1", Value: attribute.StringValue("123.5"), }, }, { name: "Int", actual: attribute.Int("k1", 123), expected: attribute.KeyValue{ Key: "k1", Value: attribute.IntValue(123), }, }, } for _, test := range tt { t.Run(test.name, func(t *testing.T) { if diff := cmp.Diff(test.actual, test.expected, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatal(diff) } }) } } func TestKeyValueValid(t *testing.T) { tests := []struct { desc string valid bool kv attribute.KeyValue }{ { desc: "uninitialized KeyValue should be invalid", valid: false, kv: attribute.KeyValue{}, }, { desc: "empty key value should be invalid", valid: false, kv: attribute.Key("").Bool(true), }, { desc: "INVALID value type should be invalid", valid: false, kv: attribute.KeyValue{ Key: attribute.Key("valid key"), // Default type is INVALID. Value: attribute.Value{}, }, }, { desc: "non-empty key with BOOL type Value should be valid", valid: true, kv: attribute.Bool("bool", true), }, { desc: "non-empty key with INT64 type Value should be valid", valid: true, kv: attribute.Int64("int64", 0), }, { desc: "non-empty key with FLOAT64 type Value should be valid", valid: true, kv: attribute.Float64("float64", 0), }, { desc: "non-empty key with STRING type Value should be valid", valid: true, kv: attribute.String("string", ""), }, } for _, test := range tests { if got, want := test.kv.Valid(), test.valid; got != want { t.Error(test.desc) } } } golang-opentelemetry-otel-1.1.0/attribute/set.go000066400000000000000000000264201414226744000217500ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" import ( "encoding/json" "reflect" "sort" ) type ( // Set is the representation for a distinct label set. It // manages an immutable set of labels, with an internal cache // for storing label encodings. // // This type supports the `Equivalent` method of comparison // using values of type `Distinct`. // // This type is used to implement: // 1. Metric labels // 2. Resource sets // 3. Correlation map (TODO) Set struct { equivalent Distinct } // Distinct wraps a variable-size array of `KeyValue`, // constructed with keys in sorted order. This can be used as // a map key or for equality checking between Sets. Distinct struct { iface interface{} } // Filter supports removing certain labels from label sets. // When the filter returns true, the label will be kept in // the filtered label set. When the filter returns false, the // label is excluded from the filtered label set, and the // label instead appears in the `removed` list of excluded labels. Filter func(KeyValue) bool // Sortable implements `sort.Interface`, used for sorting // `KeyValue`. This is an exported type to support a // memory optimization. A pointer to one of these is needed // for the call to `sort.Stable()`, which the caller may // provide in order to avoid an allocation. See // `NewSetWithSortable()`. Sortable []KeyValue ) var ( // keyValueType is used in `computeDistinctReflect`. keyValueType = reflect.TypeOf(KeyValue{}) // emptySet is returned for empty label sets. emptySet = &Set{ equivalent: Distinct{ iface: [0]KeyValue{}, }, } ) // EmptySet returns a reference to a Set with no elements. // // This is a convenience provided for optimized calling utility. func EmptySet() *Set { return emptySet } // reflect abbreviates `reflect.ValueOf`. func (d Distinct) reflect() reflect.Value { return reflect.ValueOf(d.iface) } // Valid returns true if this value refers to a valid `*Set`. func (d Distinct) Valid() bool { return d.iface != nil } // Len returns the number of labels in this set. func (l *Set) Len() int { if l == nil || !l.equivalent.Valid() { return 0 } return l.equivalent.reflect().Len() } // Get returns the KeyValue at ordered position `idx` in this set. func (l *Set) Get(idx int) (KeyValue, bool) { if l == nil { return KeyValue{}, false } value := l.equivalent.reflect() if idx >= 0 && idx < value.Len() { // Note: The Go compiler successfully avoids an allocation for // the interface{} conversion here: return value.Index(idx).Interface().(KeyValue), true } return KeyValue{}, false } // Value returns the value of a specified key in this set. func (l *Set) Value(k Key) (Value, bool) { if l == nil { return Value{}, false } rValue := l.equivalent.reflect() vlen := rValue.Len() idx := sort.Search(vlen, func(idx int) bool { return rValue.Index(idx).Interface().(KeyValue).Key >= k }) if idx >= vlen { return Value{}, false } keyValue := rValue.Index(idx).Interface().(KeyValue) if k == keyValue.Key { return keyValue.Value, true } return Value{}, false } // HasValue tests whether a key is defined in this set. func (l *Set) HasValue(k Key) bool { if l == nil { return false } _, ok := l.Value(k) return ok } // Iter returns an iterator for visiting the labels in this set. func (l *Set) Iter() Iterator { return Iterator{ storage: l, idx: -1, } } // ToSlice returns the set of labels belonging to this set, sorted, // where keys appear no more than once. func (l *Set) ToSlice() []KeyValue { iter := l.Iter() return iter.ToSlice() } // Equivalent returns a value that may be used as a map key. The // Distinct type guarantees that the result will equal the equivalent // Distinct value of any label set with the same elements as this, // where sets are made unique by choosing the last value in the input // for any given key. func (l *Set) Equivalent() Distinct { if l == nil || !l.equivalent.Valid() { return emptySet.equivalent } return l.equivalent } // Equals returns true if the argument set is equivalent to this set. func (l *Set) Equals(o *Set) bool { return l.Equivalent() == o.Equivalent() } // Encoded returns the encoded form of this set, according to // `encoder`. func (l *Set) Encoded(encoder Encoder) string { if l == nil || encoder == nil { return "" } return encoder.Encode(l.Iter()) } func empty() Set { return Set{ equivalent: emptySet.equivalent, } } // NewSet returns a new `Set`. See the documentation for // `NewSetWithSortableFiltered` for more details. // // Except for empty sets, this method adds an additional allocation // compared with calls that include a `*Sortable`. func NewSet(kvs ...KeyValue) Set { // Check for empty set. if len(kvs) == 0 { return empty() } s, _ := NewSetWithSortableFiltered(kvs, new(Sortable), nil) return s } // NewSetWithSortable returns a new `Set`. See the documentation for // `NewSetWithSortableFiltered` for more details. // // This call includes a `*Sortable` option as a memory optimization. func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { // Check for empty set. if len(kvs) == 0 { return empty() } s, _ := NewSetWithSortableFiltered(kvs, tmp, nil) return s } // NewSetWithFiltered returns a new `Set`. See the documentation for // `NewSetWithSortableFiltered` for more details. // // This call includes a `Filter` to include/exclude label keys from // the return value. Excluded keys are returned as a slice of label // values. func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { return empty(), nil } return NewSetWithSortableFiltered(kvs, new(Sortable), filter) } // NewSetWithSortableFiltered returns a new `Set`. // // Duplicate keys are eliminated by taking the last value. This // re-orders the input slice so that unique last-values are contiguous // at the end of the slice. // // This ensures the following: // // - Last-value-wins semantics // - Caller sees the reordering, but doesn't lose values // - Repeated call preserve last-value wins. // // Note that methods are defined on `*Set`, although this returns `Set`. // Callers can avoid memory allocations by: // // - allocating a `Sortable` for use as a temporary in this method // - allocating a `Set` for storing the return value of this // constructor. // // The result maintains a cache of encoded labels, by attribute.EncoderID. // This value should not be copied after its first use. // // The second `[]KeyValue` return value is a list of labels that were // excluded by the Filter (if non-nil). func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { return empty(), nil } *tmp = kvs // Stable sort so the following de-duplication can implement // last-value-wins semantics. sort.Stable(tmp) *tmp = nil position := len(kvs) - 1 offset := position - 1 // The requirements stated above require that the stable // result be placed in the end of the input slice, while // overwritten values are swapped to the beginning. // // De-duplicate with last-value-wins semantics. Preserve // duplicate values at the beginning of the input slice. for ; offset >= 0; offset-- { if kvs[offset].Key == kvs[position].Key { continue } position-- kvs[offset], kvs[position] = kvs[position], kvs[offset] } if filter != nil { return filterSet(kvs[position:], filter) } return Set{ equivalent: computeDistinct(kvs[position:]), }, nil } // filterSet reorders `kvs` so that included keys are contiguous at // the end of the slice, while excluded keys precede the included keys. func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) { var excluded []KeyValue // Move labels that do not match the filter so // they're adjacent before calling computeDistinct(). distinctPosition := len(kvs) // Swap indistinct keys forward and distinct keys toward the // end of the slice. offset := len(kvs) - 1 for ; offset >= 0; offset-- { if filter(kvs[offset]) { distinctPosition-- kvs[offset], kvs[distinctPosition] = kvs[distinctPosition], kvs[offset] continue } } excluded = kvs[:distinctPosition] return Set{ equivalent: computeDistinct(kvs[distinctPosition:]), }, excluded } // Filter returns a filtered copy of this `Set`. See the // documentation for `NewSetWithSortableFiltered` for more details. func (l *Set) Filter(re Filter) (Set, []KeyValue) { if re == nil { return Set{ equivalent: l.equivalent, }, nil } // Note: This could be refactored to avoid the temporary slice // allocation, if it proves to be expensive. return filterSet(l.ToSlice(), re) } // computeDistinct returns a `Distinct` using either the fixed- or // reflect-oriented code path, depending on the size of the input. // The input slice is assumed to already be sorted and de-duplicated. func computeDistinct(kvs []KeyValue) Distinct { iface := computeDistinctFixed(kvs) if iface == nil { iface = computeDistinctReflect(kvs) } return Distinct{ iface: iface, } } // computeDistinctFixed computes a `Distinct` for small slices. It // returns nil if the input is too large for this code path. func computeDistinctFixed(kvs []KeyValue) interface{} { switch len(kvs) { case 1: ptr := new([1]KeyValue) copy((*ptr)[:], kvs) return *ptr case 2: ptr := new([2]KeyValue) copy((*ptr)[:], kvs) return *ptr case 3: ptr := new([3]KeyValue) copy((*ptr)[:], kvs) return *ptr case 4: ptr := new([4]KeyValue) copy((*ptr)[:], kvs) return *ptr case 5: ptr := new([5]KeyValue) copy((*ptr)[:], kvs) return *ptr case 6: ptr := new([6]KeyValue) copy((*ptr)[:], kvs) return *ptr case 7: ptr := new([7]KeyValue) copy((*ptr)[:], kvs) return *ptr case 8: ptr := new([8]KeyValue) copy((*ptr)[:], kvs) return *ptr case 9: ptr := new([9]KeyValue) copy((*ptr)[:], kvs) return *ptr case 10: ptr := new([10]KeyValue) copy((*ptr)[:], kvs) return *ptr default: return nil } } // computeDistinctReflect computes a `Distinct` using reflection, // works for any size input. func computeDistinctReflect(kvs []KeyValue) interface{} { at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() for i, keyValue := range kvs { *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue } return at.Interface() } // MarshalJSON returns the JSON encoding of the `*Set`. func (l *Set) MarshalJSON() ([]byte, error) { return json.Marshal(l.equivalent.iface) } // Len implements `sort.Interface`. func (l *Sortable) Len() int { return len(*l) } // Swap implements `sort.Interface`. func (l *Sortable) Swap(i, j int) { (*l)[i], (*l)[j] = (*l)[j], (*l)[i] } // Less implements `sort.Interface`. func (l *Sortable) Less(i, j int) bool { return (*l)[i].Key < (*l)[j].Key } golang-opentelemetry-otel-1.1.0/attribute/set_test.go000066400000000000000000000125631414226744000230120ustar00rootroot00000000000000// 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 attribute_test import ( "regexp" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ) type testCase struct { kvs []attribute.KeyValue keyRe *regexp.Regexp encoding string fullEnc string } func expect(enc string, kvs ...attribute.KeyValue) testCase { return testCase{ kvs: kvs, encoding: enc, } } func expectFiltered(enc, filter, fullEnc string, kvs ...attribute.KeyValue) testCase { return testCase{ kvs: kvs, keyRe: regexp.MustCompile(filter), encoding: enc, fullEnc: fullEnc, } } func TestSetDedup(t *testing.T) { cases := []testCase{ expect("A=B", attribute.String("A", "2"), attribute.String("A", "B")), expect("A=B", attribute.String("A", "2"), attribute.Int("A", 1), attribute.String("A", "B")), expect("A=B", attribute.String("A", "B"), attribute.String("A", "C"), attribute.String("A", "D"), attribute.String("A", "B")), expect("A=B,C=D", attribute.String("A", "1"), attribute.String("C", "D"), attribute.String("A", "B")), expect("A=B,C=D", attribute.String("A", "2"), attribute.String("A", "B"), attribute.String("C", "D")), expect("A=B,C=D", attribute.Float64("C", 1.2), attribute.String("A", "2"), attribute.String("A", "B"), attribute.String("C", "D")), expect("A=B,C=D", attribute.String("C", "D"), attribute.String("A", "B"), attribute.String("A", "C"), attribute.String("A", "D"), attribute.String("A", "B")), expect("A=B,C=D", attribute.String("A", "B"), attribute.String("C", "D"), attribute.String("A", "C"), attribute.String("A", "D"), attribute.String("A", "B")), expect("A=B,C=D", attribute.String("A", "B"), attribute.String("A", "C"), attribute.String("A", "D"), attribute.String("A", "B"), attribute.String("C", "D")), } enc := attribute.DefaultEncoder() s2d := map[string][]attribute.Distinct{} d2s := map[attribute.Distinct][]string{} for _, tc := range cases { cpy := make([]attribute.KeyValue, len(tc.kvs)) copy(cpy, tc.kvs) sl := attribute.NewSet(cpy...) // Ensure that the input was reordered but no elements went missing. require.ElementsMatch(t, tc.kvs, cpy) str := sl.Encoded(enc) equ := sl.Equivalent() s2d[str] = append(s2d[str], equ) d2s[equ] = append(d2s[equ], str) require.Equal(t, tc.encoding, str) } for s, d := range s2d { // No other Distinct values are equal to this. for s2, d2 := range s2d { if s2 == s { continue } for _, elt := range d { for _, otherDistinct := range d2 { require.NotEqual(t, otherDistinct, elt) } } } for _, strings := range d2s { if strings[0] == s { continue } for _, otherString := range strings { require.NotEqual(t, otherString, s) } } } for d, s := range d2s { // No other Distinct values are equal to this. for d2, s2 := range d2s { if d2 == d { continue } for _, elt := range s { for _, otherDistinct := range s2 { require.NotEqual(t, otherDistinct, elt) } } } for _, distincts := range s2d { if distincts[0] == d { continue } for _, otherDistinct := range distincts { require.NotEqual(t, otherDistinct, d) } } } } func TestUniqueness(t *testing.T) { short := []attribute.KeyValue{ attribute.String("A", "0"), attribute.String("B", "2"), attribute.String("A", "1"), } long := []attribute.KeyValue{ attribute.String("B", "2"), attribute.String("C", "5"), attribute.String("B", "2"), attribute.String("C", "1"), attribute.String("A", "4"), attribute.String("C", "3"), attribute.String("A", "1"), } cases := []testCase{ expectFiltered("A=1", "^A$", "B=2", short...), expectFiltered("B=2", "^B$", "A=1", short...), expectFiltered("A=1,B=2", "^A|B$", "", short...), expectFiltered("", "^C", "A=1,B=2", short...), expectFiltered("A=1,C=3", "A|C", "B=2", long...), expectFiltered("B=2,C=3", "C|B", "A=1", long...), expectFiltered("C=3", "C", "A=1,B=2", long...), expectFiltered("", "D", "A=1,B=2,C=3", long...), } enc := attribute.DefaultEncoder() for _, tc := range cases { cpy := make([]attribute.KeyValue, len(tc.kvs)) copy(cpy, tc.kvs) distinct, uniq := attribute.NewSetWithFiltered(cpy, func(label attribute.KeyValue) bool { return tc.keyRe.MatchString(string(label.Key)) }) full := attribute.NewSet(uniq...) require.Equal(t, tc.encoding, distinct.Encoded(enc)) require.Equal(t, tc.fullEnc, full.Encoded(enc)) } } func TestLookup(t *testing.T) { set := attribute.NewSet(attribute.Int("C", 3), attribute.Int("A", 1), attribute.Int("B", 2)) value, has := set.Value("C") require.True(t, has) require.Equal(t, int64(3), value.AsInt64()) value, has = set.Value("B") require.True(t, has) require.Equal(t, int64(2), value.AsInt64()) value, has = set.Value("A") require.True(t, has) require.Equal(t, int64(1), value.AsInt64()) value, has = set.Value("D") require.False(t, has) } golang-opentelemetry-otel-1.1.0/attribute/type_string.go000066400000000000000000000014401414226744000235170ustar00rootroot00000000000000// Code generated by "stringer -type=Type"; DO NOT EDIT. package attribute import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[INVALID-0] _ = x[BOOL-1] _ = x[INT64-2] _ = x[FLOAT64-3] _ = x[STRING-4] _ = x[BOOLSLICE-5] _ = x[INT64SLICE-6] _ = x[FLOAT64SLICE-7] _ = x[STRINGSLICE-8] } const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE" var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { return "Type(" + strconv.FormatInt(int64(i), 10) + ")" } return _Type_name[_Type_index[i]:_Type_index[i+1]] } golang-opentelemetry-otel-1.1.0/attribute/value.go000066400000000000000000000136701414226744000222740ustar00rootroot00000000000000// 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 attribute // import "go.opentelemetry.io/otel/attribute" import ( "encoding/json" "fmt" "strconv" "go.opentelemetry.io/otel/internal" ) //go:generate stringer -type=Type // Type describes the type of the data Value holds. type Type int // Value represents the value part in key-value pairs. type Value struct { vtype Type numeric uint64 stringly string slice interface{} } const ( // INVALID is used for a Value with no value set. INVALID Type = iota // BOOL is a boolean Type Value. BOOL // INT64 is a 64-bit signed integral Type Value. INT64 // FLOAT64 is a 64-bit floating point Type Value. FLOAT64 // STRING is a string Type Value. STRING // BOOLSLICE is a slice of booleans Type Value. BOOLSLICE // INT64SLICE is a slice of 64-bit signed integral numbers Type Value. INT64SLICE // FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value. FLOAT64SLICE // STRINGSLICE is a slice of strings Type Value. STRINGSLICE ) // BoolValue creates a BOOL Value. func BoolValue(v bool) Value { return Value{ vtype: BOOL, numeric: internal.BoolToRaw(v), } } // BoolSliceValue creates a BOOLSLICE Value. func BoolSliceValue(v []bool) Value { cp := make([]bool, len(v)) copy(cp, v) return Value{ vtype: BOOLSLICE, slice: &cp, } } // IntValue creates an INT64 Value. func IntValue(v int) Value { return Int64Value(int64(v)) } // IntSliceValue creates an INTSLICE Value. func IntSliceValue(v []int) Value { cp := make([]int64, 0, len(v)) for _, i := range v { cp = append(cp, int64(i)) } return Value{ vtype: INT64SLICE, slice: &cp, } } // Int64Value creates an INT64 Value. func Int64Value(v int64) Value { return Value{ vtype: INT64, numeric: internal.Int64ToRaw(v), } } // Int64SliceValue creates an INT64SLICE Value. func Int64SliceValue(v []int64) Value { cp := make([]int64, len(v)) copy(cp, v) return Value{ vtype: INT64SLICE, slice: &cp, } } // Float64Value creates a FLOAT64 Value. func Float64Value(v float64) Value { return Value{ vtype: FLOAT64, numeric: internal.Float64ToRaw(v), } } // Float64SliceValue creates a FLOAT64SLICE Value. func Float64SliceValue(v []float64) Value { cp := make([]float64, len(v)) copy(cp, v) return Value{ vtype: FLOAT64SLICE, slice: &cp, } } // StringValue creates a STRING Value. func StringValue(v string) Value { return Value{ vtype: STRING, stringly: v, } } // StringSliceValue creates a STRINGSLICE Value. func StringSliceValue(v []string) Value { cp := make([]string, len(v)) copy(cp, v) return Value{ vtype: STRINGSLICE, slice: &cp, } } // Type returns a type of the Value. func (v Value) Type() Type { return v.vtype } // AsBool returns the bool value. Make sure that the Value's type is // BOOL. func (v Value) AsBool() bool { return internal.RawToBool(v.numeric) } // AsBoolSlice returns the []bool value. Make sure that the Value's type is // BOOLSLICE. func (v Value) AsBoolSlice() []bool { if s, ok := v.slice.(*[]bool); ok { return *s } return nil } // AsInt64 returns the int64 value. Make sure that the Value's type is // INT64. func (v Value) AsInt64() int64 { return internal.RawToInt64(v.numeric) } // AsInt64Slice returns the []int64 value. Make sure that the Value's type is // INT64SLICE. func (v Value) AsInt64Slice() []int64 { if s, ok := v.slice.(*[]int64); ok { return *s } return nil } // AsFloat64 returns the float64 value. Make sure that the Value's // type is FLOAT64. func (v Value) AsFloat64() float64 { return internal.RawToFloat64(v.numeric) } // AsFloat64Slice returns the []float64 value. Make sure that the Value's type is // INT64SLICE. func (v Value) AsFloat64Slice() []float64 { if s, ok := v.slice.(*[]float64); ok { return *s } return nil } // AsString returns the string value. Make sure that the Value's type // is STRING. func (v Value) AsString() string { return v.stringly } // AsStringSlice returns the []string value. Make sure that the Value's type is // INT64SLICE. func (v Value) AsStringSlice() []string { if s, ok := v.slice.(*[]string); ok { return *s } return nil } type unknownValueType struct{} // AsInterface returns Value's data as interface{}. func (v Value) AsInterface() interface{} { switch v.Type() { case BOOL: return v.AsBool() case BOOLSLICE: return v.AsBoolSlice() case INT64: return v.AsInt64() case INT64SLICE: return v.AsInt64Slice() case FLOAT64: return v.AsFloat64() case FLOAT64SLICE: return v.AsFloat64Slice() case STRING: return v.stringly case STRINGSLICE: return v.AsStringSlice() } return unknownValueType{} } // Emit returns a string representation of Value's data. func (v Value) Emit() string { switch v.Type() { case BOOLSLICE: return fmt.Sprint(*(v.slice.(*[]bool))) case BOOL: return strconv.FormatBool(v.AsBool()) case INT64SLICE: return fmt.Sprint(*(v.slice.(*[]int64))) case INT64: return strconv.FormatInt(v.AsInt64(), 10) case FLOAT64SLICE: return fmt.Sprint(*(v.slice.(*[]float64))) case FLOAT64: return fmt.Sprint(v.AsFloat64()) case STRINGSLICE: return fmt.Sprint(*(v.slice.(*[]string))) case STRING: return v.stringly default: return "unknown" } } // MarshalJSON returns the JSON encoding of the Value. func (v Value) MarshalJSON() ([]byte, error) { var jsonVal struct { Type string Value interface{} } jsonVal.Type = v.Type().String() jsonVal.Value = v.AsInterface() return json.Marshal(jsonVal) } golang-opentelemetry-otel-1.1.0/attribute/value_test.go000066400000000000000000000064411414226744000233310ustar00rootroot00000000000000// 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 attribute_test import ( "testing" "github.com/google/go-cmp/cmp" "go.opentelemetry.io/otel/attribute" ) func TestValue(t *testing.T) { k := attribute.Key("test") for _, testcase := range []struct { name string value attribute.Value wantType attribute.Type wantValue interface{} }{ { name: "Key.Bool() correctly returns keys's internal bool value", value: k.Bool(true).Value, wantType: attribute.BOOL, wantValue: true, }, { name: "Key.BoolSlice() correctly returns keys's internal []bool value", value: k.BoolSlice([]bool{true, false, true}).Value, wantType: attribute.BOOLSLICE, wantValue: []bool{true, false, true}, }, { name: "Key.Int64() correctly returns keys's internal int64 value", value: k.Int64(42).Value, wantType: attribute.INT64, wantValue: int64(42), }, { name: "Key.Int64Slice() correctly returns keys's internal []int64 value", value: k.Int64Slice([]int64{42, -3, 12}).Value, wantType: attribute.INT64SLICE, wantValue: []int64{42, -3, 12}, }, { name: "Key.Int() correctly returns keys's internal signed integral value", value: k.Int(42).Value, wantType: attribute.INT64, wantValue: int64(42), }, { name: "Key.IntSlice() correctly returns keys's internal []int64 value", value: k.IntSlice([]int{42, -3, 12}).Value, wantType: attribute.INT64SLICE, wantValue: []int64{42, -3, 12}, }, { name: "Key.Float64() correctly returns keys's internal float64 value", value: k.Float64(42.1).Value, wantType: attribute.FLOAT64, wantValue: 42.1, }, { name: "Key.Float64Slice() correctly returns keys's internal []float64 value", value: k.Float64Slice([]float64{42, -3, 12}).Value, wantType: attribute.FLOAT64SLICE, wantValue: []float64{42, -3, 12}, }, { name: "Key.String() correctly returns keys's internal string value", value: k.String("foo").Value, wantType: attribute.STRING, wantValue: "foo", }, { name: "Key.StringSlice() correctly returns keys's internal []string value", value: k.StringSlice([]string{"forty-two", "negative three", "twelve"}).Value, wantType: attribute.STRINGSLICE, wantValue: []string{"forty-two", "negative three", "twelve"}, }, } { t.Logf("Running test case %s", testcase.name) if testcase.value.Type() != testcase.wantType { t.Errorf("wrong value type, got %#v, expected %#v", testcase.value.Type(), testcase.wantType) } if testcase.wantType == attribute.INVALID { continue } got := testcase.value.AsInterface() if diff := cmp.Diff(testcase.wantValue, got); diff != "" { t.Errorf("+got, -want: %s", diff) } } } golang-opentelemetry-otel-1.1.0/baggage/000077500000000000000000000000001414226744000201745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/baggage/baggage.go000066400000000000000000000331301414226744000221000ustar00rootroot00000000000000// 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 baggage // import "go.opentelemetry.io/otel/baggage" import ( "errors" "fmt" "net/url" "regexp" "strings" "go.opentelemetry.io/otel/internal/baggage" ) const ( maxMembers = 180 maxBytesPerMembers = 4096 maxBytesPerBaggageString = 8192 listDelimiter = "," keyValueDelimiter = "=" propertyDelimiter = ";" keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)` valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)` keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*` ) var ( keyRe = regexp.MustCompile(`^` + keyDef + `$`) valueRe = regexp.MustCompile(`^` + valueDef + `$`) propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`) ) var ( errInvalidKey = errors.New("invalid key") errInvalidValue = errors.New("invalid value") errInvalidProperty = errors.New("invalid baggage list-member property") errInvalidMember = errors.New("invalid baggage list-member") errMemberNumber = errors.New("too many list-members in baggage-string") errMemberBytes = errors.New("list-member too large") errBaggageBytes = errors.New("baggage-string too large") ) // Property is an additional metadata entry for a baggage list-member. type Property struct { key, value string // hasValue indicates if a zero-value value means the property does not // have a value or if it was the zero-value. hasValue bool } func NewKeyProperty(key string) (Property, error) { p := Property{} if !keyRe.MatchString(key) { return p, fmt.Errorf("%w: %q", errInvalidKey, key) } p.key = key return p, nil } func NewKeyValueProperty(key, value string) (Property, error) { p := Property{} if !keyRe.MatchString(key) { return p, fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { return p, fmt.Errorf("%w: %q", errInvalidValue, value) } p.key = key p.value = value p.hasValue = true return p, nil } // parseProperty attempts to decode a Property from the passed string. It // returns an error if the input is invalid according to the W3C Baggage // specification. func parseProperty(property string) (Property, error) { p := Property{} if property == "" { return p, nil } match := propertyRe.FindStringSubmatch(property) if len(match) != 4 { return p, fmt.Errorf("%w: %q", errInvalidProperty, property) } if match[1] != "" { p.key = match[1] } else { p.key = match[2] p.value = match[3] p.hasValue = true } return p, nil } // validate ensures p conforms to the W3C Baggage specification, returning an // error otherwise. func (p Property) validate() error { errFunc := func(err error) error { return fmt.Errorf("invalid property: %w", err) } if !keyRe.MatchString(p.key) { return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key)) } if p.hasValue && !valueRe.MatchString(p.value) { return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value)) } if !p.hasValue && p.value != "" { return errFunc(errors.New("inconsistent value")) } return nil } // Key returns the Property key. func (p Property) Key() string { return p.key } // Value returns the Property value. Additionally a boolean value is returned // indicating if the returned value is the empty if the Property has a value // that is empty or if the value is not set. func (p Property) Value() (string, bool) { return p.value, p.hasValue } // String encodes Property into a string compliant with the W3C Baggage // specification. func (p Property) String() string { if p.hasValue { return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value) } return p.key } type properties []Property func fromInternalProperties(iProps []baggage.Property) properties { if len(iProps) == 0 { return nil } props := make(properties, len(iProps)) for i, p := range iProps { props[i] = Property{ key: p.Key, value: p.Value, hasValue: p.HasValue, } } return props } func (p properties) asInternal() []baggage.Property { if len(p) == 0 { return nil } iProps := make([]baggage.Property, len(p)) for i, prop := range p { iProps[i] = baggage.Property{ Key: prop.key, Value: prop.value, HasValue: prop.hasValue, } } return iProps } func (p properties) Copy() properties { if len(p) == 0 { return nil } props := make(properties, len(p)) copy(props, p) return props } // validate ensures each Property in p conforms to the W3C Baggage // specification, returning an error otherwise. func (p properties) validate() error { for _, prop := range p { if err := prop.validate(); err != nil { return err } } return nil } // String encodes properties into a string compliant with the W3C Baggage // specification. func (p properties) String() string { props := make([]string, len(p)) for i, prop := range p { props[i] = prop.String() } return strings.Join(props, propertyDelimiter) } // Member is a list-member of a baggage-string as defined by the W3C Baggage // specification. type Member struct { key, value string properties properties } // NewMember returns a new Member from the passed arguments. An error is // returned if the created Member would be invalid according to the W3C // Baggage specification. func NewMember(key, value string, props ...Property) (Member, error) { m := Member{key: key, value: value, properties: properties(props).Copy()} if err := m.validate(); err != nil { return Member{}, err } return m, nil } // parseMember attempts to decode a Member from the passed string. It returns // an error if the input is invalid according to the W3C Baggage // specification. func parseMember(member string) (Member, error) { if n := len(member); n > maxBytesPerMembers { return Member{}, fmt.Errorf("%w: %d", errMemberBytes, n) } var ( key, value string props properties ) parts := strings.SplitN(member, propertyDelimiter, 2) switch len(parts) { case 2: // Parse the member properties. for _, pStr := range strings.Split(parts[1], propertyDelimiter) { p, err := parseProperty(pStr) if err != nil { return Member{}, err } props = append(props, p) } fallthrough case 1: // Parse the member key/value pair. // Take into account a value can contain equal signs (=). kv := strings.SplitN(parts[0], keyValueDelimiter, 2) if len(kv) != 2 { return Member{}, fmt.Errorf("%w: %q", errInvalidMember, member) } // "Leading and trailing whitespaces are allowed but MUST be trimmed // when converting the header into a data structure." key, value = strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]) if !keyRe.MatchString(key) { return Member{}, fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { return Member{}, fmt.Errorf("%w: %q", errInvalidValue, value) } default: // This should never happen unless a developer has changed the string // splitting somehow. Panic instead of failing silently and allowing // the bug to slip past the CI checks. panic("failed to parse baggage member") } return Member{key: key, value: value, properties: props}, nil } // validate ensures m conforms to the W3C Baggage specification, returning an // error otherwise. func (m Member) validate() error { if !keyRe.MatchString(m.key) { return fmt.Errorf("%w: %q", errInvalidKey, m.key) } if !valueRe.MatchString(m.value) { return fmt.Errorf("%w: %q", errInvalidValue, m.value) } return m.properties.validate() } // Key returns the Member key. func (m Member) Key() string { return m.key } // Value returns the Member value. func (m Member) Value() string { return m.value } // Properties returns a copy of the Member properties. func (m Member) Properties() []Property { return m.properties.Copy() } // String encodes Member into a string compliant with the W3C Baggage // specification. func (m Member) String() string { // A key is just an ASCII string, but a value is URL encoded UTF-8. s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, url.QueryEscape(m.value)) if len(m.properties) > 0 { s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String()) } return s } // Baggage is a list of baggage members representing the baggage-string as // defined by the W3C Baggage specification. type Baggage struct { //nolint:golint list baggage.List } // New returns a new valid Baggage. It returns an error if the passed members // are invalid according to the W3C Baggage specification or if it results in // a Baggage exceeding limits set in that specification. func New(members ...Member) (Baggage, error) { if len(members) == 0 { return Baggage{}, nil } b := make(baggage.List) for _, m := range members { if err := m.validate(); err != nil { return Baggage{}, err } // OpenTelemetry resolves duplicates by last-one-wins. b[m.key] = baggage.Item{ Value: m.value, Properties: m.properties.asInternal(), } } // Check member numbers after deduplicating. if len(b) > maxMembers { return Baggage{}, errMemberNumber } bag := Baggage{b} if n := len(bag.String()); n > maxBytesPerBaggageString { return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) } return bag, nil } // Parse attempts to decode a baggage-string from the passed string. It // returns an error if the input is invalid according to the W3C Baggage // specification. // // If there are duplicate list-members contained in baggage, the last one // defined (reading left-to-right) will be the only one kept. This diverges // from the W3C Baggage specification which allows duplicate list-members, but // conforms to the OpenTelemetry Baggage specification. func Parse(bStr string) (Baggage, error) { if bStr == "" { return Baggage{}, nil } if n := len(bStr); n > maxBytesPerBaggageString { return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) } b := make(baggage.List) for _, memberStr := range strings.Split(bStr, listDelimiter) { m, err := parseMember(memberStr) if err != nil { return Baggage{}, err } // OpenTelemetry resolves duplicates by last-one-wins. b[m.key] = baggage.Item{ Value: m.value, Properties: m.properties.asInternal(), } } // OpenTelemetry does not allow for duplicate list-members, but the W3C // specification does. Now that we have deduplicated, ensure the baggage // does not exceed list-member limits. if len(b) > maxMembers { return Baggage{}, errMemberNumber } return Baggage{b}, nil } // Member returns the baggage list-member identified by key. // // If there is no list-member matching the passed key the returned Member will // be a zero-value Member. func (b Baggage) Member(key string) Member { v, ok := b.list[key] if !ok { // We do not need to worry about distiguising between the situation // where a zero-valued Member is included in the Baggage because a // zero-valued Member is invalid according to the W3C Baggage // specification (it has an empty key). return Member{} } return Member{ key: key, value: v.Value, properties: fromInternalProperties(v.Properties), } } // Members returns all the baggage list-members. // The order of the returned list-members does not have significance. func (b Baggage) Members() []Member { if len(b.list) == 0 { return nil } members := make([]Member, 0, len(b.list)) for k, v := range b.list { members = append(members, Member{ key: k, value: v.Value, properties: fromInternalProperties(v.Properties), }) } return members } // SetMember returns a copy the Baggage with the member included. If the // baggage contains a Member with the same key the existing Member is // replaced. // // If member is invalid according to the W3C Baggage specification, an error // is returned with the original Baggage. func (b Baggage) SetMember(member Member) (Baggage, error) { if err := member.validate(); err != nil { return b, fmt.Errorf("%w: %s", errInvalidMember, err) } n := len(b.list) if _, ok := b.list[member.key]; !ok { n++ } list := make(baggage.List, n) for k, v := range b.list { // Do not copy if we are just going to overwrite. if k == member.key { continue } list[k] = v } list[member.key] = baggage.Item{ Value: member.value, Properties: member.properties.asInternal(), } return Baggage{list: list}, nil } // DeleteMember returns a copy of the Baggage with the list-member identified // by key removed. func (b Baggage) DeleteMember(key string) Baggage { n := len(b.list) if _, ok := b.list[key]; ok { n-- } list := make(baggage.List, n) for k, v := range b.list { if k == key { continue } list[k] = v } return Baggage{list: list} } // Len returns the number of list-members in the Baggage. func (b Baggage) Len() int { return len(b.list) } // String encodes Baggage into a string compliant with the W3C Baggage // specification. The returned string will be invalid if the Baggage contains // any invalid list-members. func (b Baggage) String() string { members := make([]string, 0, len(b.list)) for k, v := range b.list { members = append(members, Member{ key: k, value: v.Value, properties: fromInternalProperties(v.Properties), }.String()) } return strings.Join(members, listDelimiter) } golang-opentelemetry-otel-1.1.0/baggage/baggage_test.go000066400000000000000000000372061414226744000231470ustar00rootroot00000000000000// 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 baggage import ( "fmt" "math/rand" "sort" "strings" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/internal/baggage" ) var rng *rand.Rand func init() { // Seed with a static value to ensure deterministic results. rng = rand.New(rand.NewSource(1)) } func TestKeyRegExp(t *testing.T) { // ASCII only invalidKeyRune := []rune{ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', ' ', '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', '\x7F', } for _, ch := range invalidKeyRune { assert.NotRegexp(t, keyDef, fmt.Sprintf("%c", ch)) } } func TestValueRegExp(t *testing.T) { // ASCII only invalidValueRune := []rune{ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', ' ', '"', ',', ';', '\\', '\x7F', } for _, ch := range invalidValueRune { assert.NotRegexp(t, `^`+valueDef+`$`, fmt.Sprintf("invalid-%c-value", ch)) } } func TestParseProperty(t *testing.T) { p := Property{key: "key", value: "value", hasValue: true} testcases := []struct { in string expected Property }{ { in: "", expected: Property{}, }, { in: "key", expected: Property{ key: "key", }, }, { in: "key=", expected: Property{ key: "key", hasValue: true, }, }, { in: "key=value", expected: p, }, { in: " key=value ", expected: p, }, { in: "key = value", expected: p, }, { in: " key = value ", expected: p, }, { in: "\tkey=value", expected: p, }, } for _, tc := range testcases { actual, err := parseProperty(tc.in) if !assert.NoError(t, err) { continue } assert.Equal(t, tc.expected.Key(), actual.Key(), tc.in) actualV, actualOk := actual.Value() expectedV, expectedOk := tc.expected.Value() assert.Equal(t, expectedOk, actualOk, tc.in) assert.Equal(t, expectedV, actualV, tc.in) } } func TestParsePropertyError(t *testing.T) { _, err := parseProperty(",;,") assert.ErrorIs(t, err, errInvalidProperty) } func TestNewKeyProperty(t *testing.T) { p, err := NewKeyProperty(" ") assert.ErrorIs(t, err, errInvalidKey) assert.Equal(t, Property{}, p) p, err = NewKeyProperty("key") assert.NoError(t, err) assert.Equal(t, Property{key: "key"}, p) } func TestNewKeyValueProperty(t *testing.T) { p, err := NewKeyValueProperty(" ", "") assert.ErrorIs(t, err, errInvalidKey) assert.Equal(t, Property{}, p) p, err = NewKeyValueProperty("key", ";") assert.ErrorIs(t, err, errInvalidValue) assert.Equal(t, Property{}, p) p, err = NewKeyValueProperty("key", "value") assert.NoError(t, err) assert.Equal(t, Property{key: "key", value: "value", hasValue: true}, p) } func TestPropertyValidate(t *testing.T) { p := Property{} assert.ErrorIs(t, p.validate(), errInvalidKey) p.key = "k" assert.NoError(t, p.validate()) p.value = ";" assert.EqualError(t, p.validate(), "invalid property: inconsistent value") p.hasValue = true assert.ErrorIs(t, p.validate(), errInvalidValue) p.value = "v" assert.NoError(t, p.validate()) } func TestNewEmptyBaggage(t *testing.T) { b, err := New() assert.NoError(t, err) assert.Equal(t, Baggage{}, b) } func TestNewBaggage(t *testing.T) { b, err := New(Member{key: "k"}) assert.NoError(t, err) assert.Equal(t, Baggage{list: baggage.List{"k": {}}}, b) } func TestNewBaggageWithDuplicates(t *testing.T) { // Having this many members would normally cause this to error, but since // these are duplicates of the same key they will be collapsed into a // single entry. m := make([]Member, maxMembers+1) for i := range m { // Duplicates are collapsed. m[i] = Member{ key: "a", value: fmt.Sprintf("%d", i), } } b, err := New(m...) assert.NoError(t, err) // Ensure that the last-one-wins by verifying the value. v := fmt.Sprintf("%d", maxMembers) want := Baggage{list: baggage.List{"a": {Value: v}}} assert.Equal(t, want, b) } func TestNewBaggageErrorInvalidMember(t *testing.T) { _, err := New(Member{key: ""}) assert.ErrorIs(t, err, errInvalidKey) } func key(n int) string { r := []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, n) for i := range b { b[i] = r[rng.Intn(len(r))] } return string(b) } func TestNewBaggageErrorTooManyBytes(t *testing.T) { m := make([]Member, (maxBytesPerBaggageString/maxBytesPerMembers)+1) for i := range m { m[i] = Member{key: key(maxBytesPerMembers)} } _, err := New(m...) assert.ErrorIs(t, err, errBaggageBytes) } func TestNewBaggageErrorTooManyMembers(t *testing.T) { m := make([]Member, maxMembers+1) for i := range m { m[i] = Member{key: fmt.Sprintf("%d", i)} } _, err := New(m...) assert.ErrorIs(t, err, errMemberNumber) } func TestBaggageParse(t *testing.T) { tooLarge := key(maxBytesPerBaggageString + 1) tooLargeMember := key(maxBytesPerMembers + 1) m := make([]string, maxMembers+1) for i := range m { m[i] = fmt.Sprintf("a%d=", i) } tooManyMembers := strings.Join(m, listDelimiter) testcases := []struct { name string in string want baggage.List err error }{ { name: "empty value", in: "", want: baggage.List(nil), }, { name: "single member empty value no properties", in: "foo=", want: baggage.List{ "foo": {Value: ""}, }, }, { name: "single member no properties", in: "foo=1", want: baggage.List{ "foo": {Value: "1"}, }, }, { name: "single member with spaces", in: " foo \t= 1\t\t ", want: baggage.List{ "foo": {Value: "1"}, }, }, { name: "single member empty value with properties", in: "foo=;state=on;red", want: baggage.List{ "foo": { Value: "", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, }, }, { name: "single member with properties", in: "foo=1;state=on;red", want: baggage.List{ "foo": { Value: "1", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, }, }, { name: "single member with value containing equal signs", in: "foo=0=0=0", want: baggage.List{ "foo": {Value: "0=0=0"}, }, }, { name: "two members with properties", in: "foo=1;state=on;red,bar=2;yellow", want: baggage.List{ "foo": { Value: "1", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, "bar": { Value: "2", Properties: []baggage.Property{{Key: "yellow"}}, }, }, }, { // According to the OTel spec, last value wins. name: "duplicate key", in: "foo=1;state=on;red,foo=2", want: baggage.List{ "foo": {Value: "2"}, }, }, { name: "invalid member: empty", in: "foo=,,bar=", err: errInvalidMember, }, { name: "invalid member: no key", in: "=foo", err: errInvalidKey, }, { name: "invalid member: no value", in: "foo", err: errInvalidMember, }, { name: "invalid member: invalid key", in: "\\=value", err: errInvalidKey, }, { name: "invalid member: invalid value", in: "foo=\\", err: errInvalidValue, }, { name: "invalid property: invalid key", in: "foo=1;=v", err: errInvalidProperty, }, { name: "invalid property: invalid value", in: "foo=1;key=\\", err: errInvalidProperty, }, { name: "invalid baggage string: too large", in: tooLarge, err: errBaggageBytes, }, { name: "invalid baggage string: member too large", in: tooLargeMember, err: errMemberBytes, }, { name: "invalid baggage string: too many members", in: tooManyMembers, err: errMemberNumber, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { actual, err := Parse(tc.in) assert.ErrorIs(t, err, tc.err) assert.Equal(t, Baggage{list: tc.want}, actual) }) } } func TestBaggageString(t *testing.T) { testcases := []struct { name string out string baggage baggage.List }{ { name: "empty value", out: "", baggage: baggage.List(nil), }, { name: "single member empty value no properties", out: "foo=", baggage: baggage.List{ "foo": {Value: ""}, }, }, { name: "single member no properties", out: "foo=1", baggage: baggage.List{ "foo": {Value: "1"}, }, }, { name: "URL encoded value", out: "foo=1%3D1", baggage: baggage.List{ "foo": {Value: "1=1"}, }, }, { name: "single member empty value with properties", out: "foo=;red;state=on", baggage: baggage.List{ "foo": { Value: "", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, }, }, { name: "single member with properties", // Properties are "opaque values" meaning they are sent as they // are set and no encoding is performed. out: "foo=1;red;state=on;z=z=z", baggage: baggage.List{ "foo": { Value: "1", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, {Key: "z", Value: "z=z", HasValue: true}, }, }, }, }, { name: "two members with properties", out: "bar=2;yellow,foo=1;red;state=on", baggage: baggage.List{ "foo": { Value: "1", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, "bar": { Value: "2", Properties: []baggage.Property{{Key: "yellow"}}, }, }, }, } orderer := func(s string) string { members := strings.Split(s, listDelimiter) for i, m := range members { parts := strings.Split(m, propertyDelimiter) if len(parts) > 1 { sort.Strings(parts[1:]) members[i] = strings.Join(parts, propertyDelimiter) } } sort.Strings(members) return strings.Join(members, listDelimiter) } for _, tc := range testcases { b := Baggage{tc.baggage} assert.Equal(t, tc.out, orderer(b.String())) } } func TestBaggageLen(t *testing.T) { b := Baggage{} assert.Equal(t, 0, b.Len()) b.list = make(baggage.List, 1) assert.Equal(t, 0, b.Len()) b.list["k"] = baggage.Item{} assert.Equal(t, 1, b.Len()) } func TestBaggageDeleteMember(t *testing.T) { key := "k" b0 := Baggage{} b1 := b0.DeleteMember(key) assert.NotContains(t, b1.list, key) b0 = Baggage{list: baggage.List{ key: {}, "other": {}, }} b1 = b0.DeleteMember(key) assert.Contains(t, b0.list, key) assert.NotContains(t, b1.list, key) } func TestBaggageSetMemberError(t *testing.T) { _, err := Baggage{}.SetMember(Member{}) assert.ErrorIs(t, err, errInvalidMember) } func TestBaggageSetMember(t *testing.T) { b0 := Baggage{} key := "k" m := Member{key: key} b1, err := b0.SetMember(m) assert.NoError(t, err) assert.NotContains(t, b0.list, key) assert.Equal(t, baggage.Item{}, b1.list[key]) assert.Equal(t, 0, len(b0.list)) assert.Equal(t, 1, len(b1.list)) m.value = "v" b2, err := b1.SetMember(m) assert.NoError(t, err) assert.Equal(t, baggage.Item{}, b1.list[key]) assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key]) assert.Equal(t, 1, len(b1.list)) assert.Equal(t, 1, len(b2.list)) p := properties{{key: "p"}} m.properties = p b3, err := b2.SetMember(m) assert.NoError(t, err) assert.Equal(t, baggage.Item{Value: "v"}, b2.list[key]) assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key]) assert.Equal(t, 1, len(b2.list)) assert.Equal(t, 1, len(b3.list)) // The returned baggage needs to be immutable and should use a copy of the // properties slice. p[0] = Property{key: "different"} assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key]) // Reset for below. p[0] = Property{key: "p"} m = Member{key: "another"} b4, err := b3.SetMember(m) assert.NoError(t, err) assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b3.list[key]) assert.NotContains(t, b3.list, m.key) assert.Equal(t, baggage.Item{Value: "v", Properties: []baggage.Property{{Key: "p"}}}, b4.list[key]) assert.Equal(t, baggage.Item{}, b4.list[m.key]) assert.Equal(t, 1, len(b3.list)) assert.Equal(t, 2, len(b4.list)) } func TestNilBaggageMembers(t *testing.T) { assert.Nil(t, Baggage{}.Members()) } func TestBaggageMembers(t *testing.T) { members := []Member{ { key: "foo", value: "1", properties: properties{ {key: "state", value: "on", hasValue: true}, {key: "red"}, }, }, { key: "bar", value: "2", properties: properties{ {key: "yellow"}, }, }, } baggage := Baggage{list: baggage.List{ "foo": { Value: "1", Properties: []baggage.Property{ {Key: "state", Value: "on", HasValue: true}, {Key: "red"}, }, }, "bar": { Value: "2", Properties: []baggage.Property{{Key: "yellow"}}, }, }} assert.ElementsMatch(t, members, baggage.Members()) } func TestBaggageMember(t *testing.T) { baggage := Baggage{list: baggage.List{"foo": {Value: "1"}}} assert.Equal(t, Member{key: "foo", value: "1"}, baggage.Member("foo")) assert.Equal(t, Member{}, baggage.Member("bar")) } func TestMemberKey(t *testing.T) { m := Member{} assert.Equal(t, "", m.Key(), "even invalid values should be returned") key := "k" m.key = key assert.Equal(t, key, m.Key()) } func TestMemberValue(t *testing.T) { m := Member{key: "k", value: "\\"} assert.Equal(t, "\\", m.Value(), "even invalid values should be returned") value := "v" m.value = value assert.Equal(t, value, m.Value()) } func TestMemberProperties(t *testing.T) { m := Member{key: "k", value: "v"} assert.Nil(t, m.Properties()) p := []Property{{key: "foo"}} m.properties = properties(p) got := m.Properties() assert.Equal(t, p, got) // Returned slice needs to be a copy so the original is immutable. got[0] = Property{key: "bar"} assert.NotEqual(t, m.properties, got) } func TestMemberValidation(t *testing.T) { m := Member{} assert.ErrorIs(t, m.validate(), errInvalidKey) m.key, m.value = "k", "\\" assert.ErrorIs(t, m.validate(), errInvalidValue) m.value = "v" assert.NoError(t, m.validate()) } func TestNewMember(t *testing.T) { m, err := NewMember("", "") assert.ErrorIs(t, err, errInvalidKey) assert.Equal(t, Member{}, m) key, val := "k", "v" p := Property{key: "foo"} m, err = NewMember(key, val, p) assert.NoError(t, err) expected := Member{key: key, value: val, properties: properties{{key: "foo"}}} assert.Equal(t, expected, m) // Ensure new member is immutable. p.key = "bar" assert.Equal(t, expected, m) } func TestPropertiesValidate(t *testing.T) { p := properties{{}} assert.ErrorIs(t, p.validate(), errInvalidKey) p[0].key = "foo" assert.NoError(t, p.validate()) p = append(p, Property{key: "bar"}) assert.NoError(t, p.validate()) } golang-opentelemetry-otel-1.1.0/baggage/context.go000066400000000000000000000026571414226744000222210ustar00rootroot00000000000000// 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 baggage // import "go.opentelemetry.io/otel/baggage" import ( "context" "go.opentelemetry.io/otel/internal/baggage" ) // ContextWithBaggage returns a copy of parent with baggage. func ContextWithBaggage(parent context.Context, b Baggage) context.Context { // Delegate so any hooks for the OpenTracing bridge are handled. return baggage.ContextWithList(parent, b.list) } // ContextWithoutBaggage returns a copy of parent with no baggage. func ContextWithoutBaggage(parent context.Context) context.Context { // Delegate so any hooks for the OpenTracing bridge are handled. return baggage.ContextWithList(parent, nil) } // FromContext returns the baggage contained in ctx. func FromContext(ctx context.Context) Baggage { // Delegate so any hooks for the OpenTracing bridge are handled. return Baggage{list: baggage.ListFromContext(ctx)} } golang-opentelemetry-otel-1.1.0/baggage/context_test.go000066400000000000000000000020531414226744000232460ustar00rootroot00000000000000// 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 baggage import ( "context" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/internal/baggage" ) func TestContext(t *testing.T) { ctx := context.Background() assert.Equal(t, Baggage{}, FromContext(ctx)) b := Baggage{list: baggage.List{"key": baggage.Item{Value: "val"}}} ctx = ContextWithBaggage(ctx, b) assert.Equal(t, b, FromContext(ctx)) ctx = ContextWithoutBaggage(ctx) assert.Equal(t, Baggage{}, FromContext(ctx)) } golang-opentelemetry-otel-1.1.0/baggage/doc.go000066400000000000000000000015131414226744000212700ustar00rootroot00000000000000// 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 baggage provides functionality for storing and retrieving baggage items in Go context. For propagating the baggage, see the go.opentelemetry.io/otel/propagation package. */ package baggage // import "go.opentelemetry.io/otel/baggage" golang-opentelemetry-otel-1.1.0/bridge/000077500000000000000000000000001414226744000200535ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/000077500000000000000000000000001414226744000222355ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/README.md000066400000000000000000000121611414226744000235150ustar00rootroot00000000000000# OpenCensus Bridge The OpenCensus Bridge helps facilitate the migration of an application from OpenCensus to OpenTelemetry. ## Caveat about OpenCensus Installing a metric or tracing bridge will cause OpenCensus telemetry to be exported by OpenTelemetry exporters. Since OpenCensus telemetry uses globals, installing a bridge will result in telemetry collection from _all_ libraries that use OpenCensus, including some you may not expect. For example ([#1928](https://github.com/open-telemetry/opentelemetry-go/issues/1928)), if a client library generates traces with OpenCensus, installing the bridge will cause those traces to be exported by OpenTelemetry. ## Tracing ### The Problem: Mixing OpenCensus and OpenTelemetry libraries In a perfect world, one would simply migrate their entire go application --including custom instrumentation, libraries, and exporters-- from OpenCensus to OpenTelemetry all at once. In the real world, dependency constraints, third-party ownership of libraries, or other reasons may require mixing OpenCensus and OpenTelemetry libraries in a single application. However, if you create the following spans in a go application: ```go ctx, ocSpan := opencensus.StartSpan(context.Background(), "OuterSpan") defer ocSpan.End() ctx, otSpan := opentelemetryTracer.Start(ctx, "MiddleSpan") defer otSpan.End() ctx, ocSpan := opencensus.StartSpan(ctx, "InnerSpan") defer ocSpan.End() ``` OpenCensus reports (to OpenCensus exporters): ``` [--------OuterSpan------------] [----InnerSpan------] ``` OpenTelemetry reports (to OpenTelemetry exporters): ``` [-----MiddleSpan--------] ``` Instead, I would prefer (to a single set of exporters): ``` [--------OuterSpan------------] [-----MiddleSpan--------] [----InnerSpan------] ``` ### The bridge solution The bridge implements the OpenCensus trace API using OpenTelemetry. This would cause, for example, a span recorded with OpenCensus' `StartSpan()` method to be equivalent to recording a span using OpenTelemetry's `tracer.Start()` method. Funneling all tracing API calls to OpenTelemetry APIs results in the desired unified span hierarchy. ### User Journey Starting from an application using entirely OpenCensus APIs: 1. Instantiate OpenTelemetry SDK and Exporters 2. Override OpenCensus' DefaultTracer with the bridge 3. Migrate libraries individually from OpenCensus to OpenTelemetry 4. Remove OpenCensus exporters and configuration To override OpenCensus' DefaultTracer with the bridge: ```go import ( octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/bridge/opencensus" "go.opentelemetry.io/otel" ) tracer := otel.GetTracerProvider().Tracer("bridge") octrace.DefaultTracer = opencensus.NewTracer(tracer) ``` Be sure to set the `Tracer` name to your instrumentation package name instead of `"bridge"`. #### Incompatibilities OpenCensus and OpenTelemetry APIs are not entirely compatible. If the bridge finds any incompatibilities, it will log them. Incompatibilities include: * Custom OpenCensus Samplers specified during StartSpan are ignored. * Links cannot be added to OpenCensus spans. * OpenTelemetry Debug or Deferred trace flags are dropped after an OpenCensus span is created. ## Metrics ### The problem: mixing libraries without mixing pipelines The problem for monitoring is simpler than the problem for tracing, since there are no context propagation issues to deal with. However, it still is difficult for users to migrate an entire applications' monitoring at once. It should be possible to send metrics generated by OpenCensus libraries to an OpenTelemetry pipeline so that migrating a metric does not require maintaining separate export pipelines for OpenCensus and OpenTelemetry. ### The Exporter "wrapper" solution The solution we use here is to allow wrapping an OpenTelemetry exporter such that it implements the OpenCensus exporter interfaces. This allows a single exporter to be used for metrics from *both* OpenCensus and OpenTelemetry. ### User Journey Starting from an application using entirely OpenCensus APIs: 1. Instantiate OpenTelemetry SDK and Exporters. 2. Replace OpenCensus exporters with a wrapped OpenTelemetry exporter from step 1. 3. Migrate libraries individually from OpenCensus to OpenTelemetry 4. Remove OpenCensus Exporters and configuration. For example, to swap out the OpenCensus logging exporter for the OpenTelemetry stdout exporter: ```go import ( "go.opencensus.io/metric/metricexport" "go.opentelemetry.io/otel/bridge/opencensus" "go.opentelemetry.io/otel/exporters/stdout" "go.opentelemetry.io/otel" ) // With OpenCensus, you could have previously configured the logging exporter like this: // import logexporter "go.opencensus.io/examples/exporter" // exporter, _ := logexporter.NewLogExporter(logexporter.Options{}) // Instead, we can create an equivalent using the OpenTelemetry stdout exporter: openTelemetryExporter, _ := stdout.New(stdout.WithPrettyPrint()) exporter := opencensus.NewMetricExporter(openTelemetryExporter) // Use the wrapped OpenTelemetry exporter like you normally would with OpenCensus intervalReader, _ := metricexport.NewIntervalReader(&metricexport.Reader{}, exporter) intervalReader.Start() ``` golang-opentelemetry-otel-1.1.0/bridge/opencensus/aggregation.go000066400000000000000000000124401414226744000250540ustar00rootroot00000000000000// 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 opencensus // import "go.opentelemetry.io/otel/bridge/opencensus" import ( "errors" "fmt" "time" "go.opencensus.io/metric/metricdata" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" ) var ( errIncompatibleType = errors.New("incompatible type for aggregation") errEmpty = errors.New("points may not be empty") errBadPoint = errors.New("point cannot be converted") ) // aggregationWithEndTime is an aggregation that can also provide the timestamp // of the last recorded point. type aggregationWithEndTime interface { aggregation.Aggregation end() time.Time } // newAggregationFromPoints creates an OpenTelemetry aggregation from // OpenCensus points. Points may not be empty and must be either // all (int|float)64 or all *metricdata.Distribution. func newAggregationFromPoints(points []metricdata.Point) (aggregationWithEndTime, error) { if len(points) == 0 { return nil, errEmpty } switch t := points[0].Value.(type) { case int64: return newExactAggregator(points) case float64: return newExactAggregator(points) case *metricdata.Distribution: return newDistributionAggregator(points) default: // TODO add *metricdata.Summary support return nil, fmt.Errorf("%w: %v", errIncompatibleType, t) } } var _ aggregation.Aggregation = &ocExactAggregator{} var _ aggregation.LastValue = &ocExactAggregator{} var _ aggregation.Points = &ocExactAggregator{} // newExactAggregator creates an OpenTelemetry aggreation from OpenCensus points. // Points may not be empty, and must only contain integers or floats. func newExactAggregator(pts []metricdata.Point) (aggregationWithEndTime, error) { points := make([]aggregation.Point, len(pts)) for i, pt := range pts { switch t := pt.Value.(type) { case int64: points[i] = aggregation.Point{ Number: number.NewInt64Number(pt.Value.(int64)), Time: pt.Time, } case float64: points[i] = aggregation.Point{ Number: number.NewFloat64Number(pt.Value.(float64)), Time: pt.Time, } default: return nil, fmt.Errorf("%w: %v", errIncompatibleType, t) } } return &ocExactAggregator{ points: points, }, nil } type ocExactAggregator struct { points []aggregation.Point } // Kind returns the kind of aggregation this is. func (o *ocExactAggregator) Kind() aggregation.Kind { return aggregation.ExactKind } // Points returns access to the raw data set. func (o *ocExactAggregator) Points() ([]aggregation.Point, error) { return o.points, nil } // LastValue returns the last point. func (o *ocExactAggregator) LastValue() (number.Number, time.Time, error) { last := o.points[len(o.points)-1] return last.Number, last.Time, nil } // end returns the timestamp of the last point func (o *ocExactAggregator) end() time.Time { _, t, _ := o.LastValue() return t } var _ aggregation.Aggregation = &ocDistAggregator{} var _ aggregation.Histogram = &ocDistAggregator{} // newDistributionAggregator creates an OpenTelemetry aggreation from // OpenCensus points. Points may not be empty, and must only contain // Distributions. The most recent disribution will be used in the aggregation. func newDistributionAggregator(pts []metricdata.Point) (aggregationWithEndTime, error) { // only use the most recent datapoint for now. pt := pts[len(pts)-1] val, ok := pt.Value.(*metricdata.Distribution) if !ok { return nil, fmt.Errorf("%w: %v", errBadPoint, pt.Value) } bucketCounts := make([]uint64, len(val.Buckets)) for i, bucket := range val.Buckets { if bucket.Count < 0 { return nil, fmt.Errorf("%w: bucket count may not be negative", errBadPoint) } bucketCounts[i] = uint64(bucket.Count) } if val.Count < 0 { return nil, fmt.Errorf("%w: count may not be negative", errBadPoint) } return &ocDistAggregator{ sum: number.NewFloat64Number(val.Sum), count: uint64(val.Count), buckets: aggregation.Buckets{ Boundaries: val.BucketOptions.Bounds, Counts: bucketCounts, }, endTime: pts[len(pts)-1].Time, }, nil } type ocDistAggregator struct { sum number.Number count uint64 buckets aggregation.Buckets endTime time.Time } // Kind returns the kind of aggregation this is. func (o *ocDistAggregator) Kind() aggregation.Kind { return aggregation.HistogramKind } // Sum returns the sum of values. func (o *ocDistAggregator) Sum() (number.Number, error) { return o.sum, nil } // Count returns the number of values. func (o *ocDistAggregator) Count() (uint64, error) { return o.count, nil } // Histogram returns the count of events in pre-determined buckets. func (o *ocDistAggregator) Histogram() (aggregation.Buckets, error) { return o.buckets, nil } // end returns the time the histogram was measured. func (o *ocDistAggregator) end() time.Time { return o.endTime } golang-opentelemetry-otel-1.1.0/bridge/opencensus/aggregation_test.go000066400000000000000000000215121414226744000261130ustar00rootroot00000000000000// 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 opencensus import ( "errors" "testing" "time" "go.opencensus.io/metric/metricdata" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" ) func TestNewAggregationFromPoints(t *testing.T) { now := time.Now() for _, tc := range []struct { desc string input []metricdata.Point expectedKind aggregation.Kind expectedErr error }{ { desc: "no points", expectedErr: errEmpty, }, { desc: "int point", input: []metricdata.Point{ { Time: now, Value: int64(23), }, }, expectedKind: aggregation.ExactKind, }, { desc: "float point", input: []metricdata.Point{ { Time: now, Value: float64(23), }, }, expectedKind: aggregation.ExactKind, }, { desc: "distribution point", input: []metricdata.Point{ { Time: now, Value: &metricdata.Distribution{ Count: 2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 1}, {Count: 1}, }, }, }, }, expectedKind: aggregation.HistogramKind, }, { desc: "bad distribution bucket count", input: []metricdata.Point{ { Time: now, Value: &metricdata.Distribution{ Count: 2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ // negative bucket {Count: -1}, {Count: 1}, }, }, }, }, expectedErr: errBadPoint, }, { desc: "bad distribution count", input: []metricdata.Point{ { Time: now, Value: &metricdata.Distribution{ // negative count Count: -2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 1}, {Count: 1}, }, }, }, }, expectedErr: errBadPoint, }, { desc: "incompatible point type bool", input: []metricdata.Point{ { Time: now, Value: true, }, }, expectedErr: errIncompatibleType, }, { desc: "dist is incompatible with exact", input: []metricdata.Point{ { Time: now, Value: int64(23), }, { Time: now, Value: &metricdata.Distribution{ Count: 2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 1}, {Count: 1}, }, }, }, }, expectedErr: errIncompatibleType, }, { desc: "int point is incompatible with dist", input: []metricdata.Point{ { Time: now, Value: &metricdata.Distribution{ Count: 2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 1}, {Count: 1}, }, }, }, { Time: now, Value: int64(23), }, }, expectedErr: errBadPoint, }, } { t.Run(tc.desc, func(t *testing.T) { output, err := newAggregationFromPoints(tc.input) if !errors.Is(err, tc.expectedErr) { t.Errorf("newAggregationFromPoints(%v) = err(%v), want err(%v)", tc.input, err, tc.expectedErr) } if tc.expectedErr == nil && output.Kind() != tc.expectedKind { t.Errorf("newAggregationFromPoints(%v) = %v, want %v", tc.input, output.Kind(), tc.expectedKind) } }) } } func TestPointsAggregation(t *testing.T) { now := time.Now() input := []metricdata.Point{ {Value: int64(15)}, {Value: int64(-23), Time: now}, } output, err := newAggregationFromPoints(input) if err != nil { t.Fatalf("newAggregationFromPoints(%v) = err(%v), want ", input, err) } if output.Kind() != aggregation.ExactKind { t.Errorf("newAggregationFromPoints(%v) = %v, want %v", input, output.Kind(), aggregation.ExactKind) } if output.end() != now { t.Errorf("newAggregationFromPoints(%v).end() = %v, want %v", input, output.end(), now) } pointsAgg, ok := output.(aggregation.Points) if !ok { t.Errorf("newAggregationFromPoints(%v) = %v does not implement the aggregation.Points interface", input, output) } points, err := pointsAgg.Points() if err != nil { t.Fatalf("Unexpected err: %v", err) } if len(points) != len(input) { t.Fatalf("newAggregationFromPoints(%v) resulted in %d points, want %d points", input, len(points), len(input)) } for i := range points { inputPoint := input[i] outputPoint := points[i] if inputPoint.Value != outputPoint.AsInt64() { t.Errorf("newAggregationFromPoints(%v)[%d] = %v, want %v", input, i, outputPoint.AsInt64(), inputPoint.Value) } } } func TestLastValueAggregation(t *testing.T) { now := time.Now() input := []metricdata.Point{ {Value: int64(15)}, {Value: int64(-23), Time: now}, } output, err := newAggregationFromPoints(input) if err != nil { t.Fatalf("newAggregationFromPoints(%v) = err(%v), want ", input, err) } if output.Kind() != aggregation.ExactKind { t.Errorf("newAggregationFromPoints(%v) = %v, want %v", input, output.Kind(), aggregation.ExactKind) } if output.end() != now { t.Errorf("newAggregationFromPoints(%v).end() = %v, want %v", input, output.end(), now) } lvAgg, ok := output.(aggregation.LastValue) if !ok { t.Errorf("newAggregationFromPoints(%v) = %v does not implement the aggregation.Points interface", input, output) } num, endTime, err := lvAgg.LastValue() if err != nil { t.Fatalf("Unexpected err: %v", err) } if endTime != now { t.Errorf("newAggregationFromPoints(%v).LastValue() = endTime: %v, want %v", input, endTime, now) } if num.AsInt64() != int64(-23) { t.Errorf("newAggregationFromPoints(%v).LastValue() = number: %v, want %v", input, num.AsInt64(), int64(-23)) } } func TestHistogramAggregation(t *testing.T) { now := time.Now() input := []metricdata.Point{ { Value: &metricdata.Distribution{ Count: 0, Sum: 0, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 0}, {Count: 0}, }, }, }, { Time: now, Value: &metricdata.Distribution{ Count: 2, Sum: 55, BucketOptions: &metricdata.BucketOptions{ Bounds: []float64{20, 30}, }, Buckets: []metricdata.Bucket{ {Count: 1}, {Count: 1}, }, }, }, } output, err := newAggregationFromPoints(input) if err != nil { t.Fatalf("newAggregationFromPoints(%v) = err(%v), want ", input, err) } if output.Kind() != aggregation.HistogramKind { t.Errorf("newAggregationFromPoints(%v) = %v, want %v", input, output.Kind(), aggregation.HistogramKind) } if output.end() != now { t.Errorf("newAggregationFromPoints(%v).end() = %v, want %v", input, output.end(), now) } distAgg, ok := output.(aggregation.Histogram) if !ok { t.Errorf("newAggregationFromPoints(%v) = %v does not implement the aggregation.Points interface", input, output) } sum, err := distAgg.Sum() if err != nil { t.Fatalf("Unexpected err: %v", err) } if sum.AsFloat64() != float64(55) { t.Errorf("newAggregationFromPoints(%v).Sum() = %v, want %v", input, sum.AsFloat64(), float64(55)) } count, err := distAgg.Count() if err != nil { t.Fatalf("Unexpected err: %v", err) } if count != 2 { t.Errorf("newAggregationFromPoints(%v).Count() = %v, want %v", input, count, 2) } hist, err := distAgg.Histogram() if err != nil { t.Fatalf("Unexpected err: %v", err) } inputBucketBoundaries := []float64{20, 30} if len(hist.Boundaries) != len(inputBucketBoundaries) { t.Fatalf("newAggregationFromPoints(%v).Histogram() produced %d boundaries, want %d boundaries", input, len(hist.Boundaries), len(inputBucketBoundaries)) } for i, b := range hist.Boundaries { if b != inputBucketBoundaries[i] { t.Errorf("newAggregationFromPoints(%v).Histogram().Boundaries[%d] = %v, want %v", input, i, b, inputBucketBoundaries[i]) } } inputBucketCounts := []uint64{1, 1} if len(hist.Counts) != len(inputBucketCounts) { t.Fatalf("newAggregationFromPoints(%v).Histogram() produced %d buckets, want %d buckets", input, len(hist.Counts), len(inputBucketCounts)) } for i, c := range hist.Counts { if c != inputBucketCounts[i] { t.Errorf("newAggregationFromPoints(%v).Histogram().Counts[%d] = %d, want %d", input, i, c, inputBucketCounts[i]) } } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/bridge.go000066400000000000000000000033041414226744000240200ustar00rootroot00000000000000// 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 opencensus // import "go.opentelemetry.io/otel/bridge/opencensus" import ( octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/bridge/opencensus/internal" "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" "go.opentelemetry.io/otel/trace" ) // NewTracer returns an implementation of the OpenCensus Tracer interface which // uses OpenTelemetry APIs. Using this implementation of Tracer "upgrades" // libraries that use OpenCensus to OpenTelemetry to facilitate a migration. func NewTracer(tracer trace.Tracer) octrace.Tracer { return internal.NewTracer(tracer) } // OTelSpanContextToOC converts from an OpenTelemetry SpanContext to an // OpenCensus SpanContext, and handles any incompatibilities with the global // error handler. func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext { return otel2oc.SpanContext(sc) } // OCSpanContextToOTel converts from an OpenCensus SpanContext to an // OpenTelemetry SpanContext. func OCSpanContextToOTel(sc octrace.SpanContext) trace.SpanContext { return oc2otel.SpanContext(sc) } golang-opentelemetry-otel-1.1.0/bridge/opencensus/doc.go000066400000000000000000000033511414226744000233330ustar00rootroot00000000000000// 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 opencensus provides a migration bridge from OpenCensus to // OpenTelemetry. The NewTracer function should be used to create an // OpenCensus Tracer from an OpenTelemetry Tracer. This Tracer can be use in // place of any existing OpenCensus Tracer and will generate OpenTelemetry // spans for traces. These spans will be exported by the OpenTelemetry // TracerProvider the original OpenTelemetry Tracer came from. // // There are known limitations to this bridge: // // - The AddLink method for OpenCensus Spans is not compatible with the // OpenTelemetry Span. No link can be added to an OpenTelemetry Span once it // is started. Any calls to this method for the OpenCensus Span will result // in an error being sent to the OpenTelemetry default ErrorHandler. // // - The NewContext method of the OpenCensus Tracer cannot embed an OpenCensus // Span in a context unless that Span was created by that Tracer. // // - Conversion of custom OpenCensus Samplers to OpenTelemetry is not // implemented. An error will be sent to the OpenTelemetry default // ErrorHandler if this is attempted. package opencensus // import "go.opentelemetry.io/otel/bridge/opencensus" golang-opentelemetry-otel-1.1.0/bridge/opencensus/exporter.go000066400000000000000000000134451414226744000244430ustar00rootroot00000000000000// 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 opencensus // import "go.opentelemetry.io/otel/bridge/opencensus" import ( "context" "errors" "fmt" "sync" "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricexport" ocresource "go.opencensus.io/resource" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/metric/unit" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) var errConversion = errors.New("Unable to convert from OpenCensus to OpenTelemetry") // NewMetricExporter returns an OpenCensus exporter that exports to an // OpenTelemetry exporter func NewMetricExporter(base export.Exporter) metricexport.Exporter { return &exporter{base: base} } // exporter implements the OpenCensus metric Exporter interface using an // OpenTelemetry base exporter. type exporter struct { base export.Exporter } // ExportMetrics implements the OpenCensus metric Exporter interface func (e *exporter) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error { res := resource.Empty() if len(metrics) != 0 { res = convertResource(metrics[0].Resource) } return e.base.Export(ctx, res, &censusLibraryReader{metrics: metrics}) } type censusLibraryReader struct { metrics []*metricdata.Metric } func (r censusLibraryReader) ForEach(readerFunc func(instrumentation.Library, export.Reader) error) error { return readerFunc(instrumentation.Library{ Name: "OpenCensus Bridge", }, &metricReader{metrics: r.metrics}) } type metricReader struct { // RWMutex implements locking for the `Reader` interface. sync.RWMutex metrics []*metricdata.Metric } var _ export.Reader = &metricReader{} // ForEach iterates through the metrics data, synthesizing an // export.Record with the appropriate aggregation for the exporter. func (d *metricReader) ForEach(_ aggregation.TemporalitySelector, f func(export.Record) error) error { for _, m := range d.metrics { descriptor, err := convertDescriptor(m.Descriptor) if err != nil { otel.Handle(err) continue } for _, ts := range m.TimeSeries { if len(ts.Points) == 0 { continue } ls, err := convertLabels(m.Descriptor.LabelKeys, ts.LabelValues) if err != nil { otel.Handle(err) continue } agg, err := newAggregationFromPoints(ts.Points) if err != nil { otel.Handle(err) continue } if err := f(export.NewRecord( &descriptor, &ls, agg, ts.StartTime, agg.end(), )); err != nil && !errors.Is(err, aggregation.ErrNoData) { return err } } } return nil } // convertLabels converts from OpenCensus label keys and values to an // OpenTelemetry label Set. func convertLabels(keys []metricdata.LabelKey, values []metricdata.LabelValue) (attribute.Set, error) { if len(keys) != len(values) { return attribute.NewSet(), fmt.Errorf("%w different number of label keys (%d) and values (%d)", errConversion, len(keys), len(values)) } labels := []attribute.KeyValue{} for i, lv := range values { if !lv.Present { continue } labels = append(labels, attribute.KeyValue{ Key: attribute.Key(keys[i].Key), Value: attribute.StringValue(lv.Value), }) } return attribute.NewSet(labels...), nil } // convertResource converts an OpenCensus Resource to an OpenTelemetry Resource // Note: the ocresource.Resource Type field is not used. func convertResource(res *ocresource.Resource) *resource.Resource { labels := []attribute.KeyValue{} if res == nil { return nil } for k, v := range res.Labels { labels = append(labels, attribute.KeyValue{Key: attribute.Key(k), Value: attribute.StringValue(v)}) } return resource.NewSchemaless(labels...) } // convertDescriptor converts an OpenCensus Descriptor to an OpenTelemetry Descriptor func convertDescriptor(ocDescriptor metricdata.Descriptor) (sdkapi.Descriptor, error) { var ( nkind number.Kind ikind sdkapi.InstrumentKind ) switch ocDescriptor.Type { case metricdata.TypeGaugeInt64: nkind = number.Int64Kind ikind = sdkapi.GaugeObserverInstrumentKind case metricdata.TypeGaugeFloat64: nkind = number.Float64Kind ikind = sdkapi.GaugeObserverInstrumentKind case metricdata.TypeCumulativeInt64: nkind = number.Int64Kind ikind = sdkapi.CounterObserverInstrumentKind case metricdata.TypeCumulativeFloat64: nkind = number.Float64Kind ikind = sdkapi.CounterObserverInstrumentKind default: // Includes TypeGaugeDistribution, TypeCumulativeDistribution, TypeSummary return sdkapi.Descriptor{}, fmt.Errorf("%w; descriptor type: %v", errConversion, ocDescriptor.Type) } opts := []metric.InstrumentOption{ metric.WithDescription(ocDescriptor.Description), } switch ocDescriptor.Unit { case metricdata.UnitDimensionless: opts = append(opts, metric.WithUnit(unit.Dimensionless)) case metricdata.UnitBytes: opts = append(opts, metric.WithUnit(unit.Bytes)) case metricdata.UnitMilliseconds: opts = append(opts, metric.WithUnit(unit.Milliseconds)) } cfg := metric.NewInstrumentConfig(opts...) return sdkapi.NewDescriptor(ocDescriptor.Name, ikind, nkind, cfg.Description(), cfg.Unit()), nil } golang-opentelemetry-otel-1.1.0/bridge/opencensus/exporter_test.go000066400000000000000000000324201414226744000254740ustar00rootroot00000000000000// 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 opencensus import ( "context" "errors" "fmt" "testing" "time" "go.opentelemetry.io/otel" "go.opencensus.io/metric/metricdata" ocresource "go.opencensus.io/resource" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/metric/unit" export "go.opentelemetry.io/otel/sdk/export/metric" exportmetric "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" "go.opentelemetry.io/otel/sdk/resource" ) type fakeExporter struct { export.Exporter records []export.Record resource *resource.Resource err error } func (f *fakeExporter) Export(ctx context.Context, res *resource.Resource, ilr exportmetric.InstrumentationLibraryReader) error { return controllertest.ReadAll(ilr, aggregation.StatelessTemporalitySelector(), func(_ instrumentation.Library, record exportmetric.Record) error { f.resource = res f.records = append(f.records, record) return f.err }) } type fakeErrorHandler struct { err error } func (f *fakeErrorHandler) Handle(err error) { f.err = err } func (f *fakeErrorHandler) matches(err error) error { // make sure err is cleared for the next test defer func() { f.err = nil }() if !errors.Is(f.err, err) { return fmt.Errorf("err(%v), want err(%v)", f.err, err) } return nil } func TestExportMetrics(t *testing.T) { now := time.Now() basicDesc := metrictest.NewDescriptor( "", sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, ) fakeErrorHandler := &fakeErrorHandler{} otel.SetErrorHandler(fakeErrorHandler) for _, tc := range []struct { desc string input []*metricdata.Metric exportErr error expected []export.Record expectedResource *resource.Resource expectedHandledError error }{ { desc: "no metrics", }, { desc: "metric without points is dropped", input: []*metricdata.Metric{ { TimeSeries: []*metricdata.TimeSeries{ {}, }, }, }, }, { desc: "descriptor conversion error", input: []*metricdata.Metric{ // TypeGaugeDistribution isn't supported {Descriptor: metricdata.Descriptor{Type: metricdata.TypeGaugeDistribution}}, }, expectedHandledError: errConversion, }, { desc: "labels conversion error", input: []*metricdata.Metric{ { // No descriptor with label keys. TimeSeries: []*metricdata.TimeSeries{ // 1 label value, which doens't exist in keys. { LabelValues: []metricdata.LabelValue{{Value: "foo", Present: true}}, Points: []metricdata.Point{ {}, }, }, }, }, }, expectedHandledError: errConversion, }, { desc: "unsupported summary point type", input: []*metricdata.Metric{ { TimeSeries: []*metricdata.TimeSeries{ { Points: []metricdata.Point{ {Value: &metricdata.Summary{}}, }, }, }, }, }, expectedHandledError: errIncompatibleType, }, { desc: "success", input: []*metricdata.Metric{ { Resource: &ocresource.Resource{ Labels: map[string]string{ "R1": "V1", "R2": "V2", }, }, TimeSeries: []*metricdata.TimeSeries{ { StartTime: now, Points: []metricdata.Point{ {Value: int64(123), Time: now}, }, }, }, }, }, expectedResource: resource.NewSchemaless( attribute.String("R1", "V1"), attribute.String("R2", "V2"), ), expected: []export.Record{ export.NewRecord( &basicDesc, attribute.EmptySet(), &ocExactAggregator{ points: []aggregation.Point{ { Number: number.NewInt64Number(123), Time: now, }, }, }, now, now, ), }, }, { desc: "export error after success", input: []*metricdata.Metric{ { TimeSeries: []*metricdata.TimeSeries{ { StartTime: now, Points: []metricdata.Point{ {Value: int64(123), Time: now}, }, }, }, }, }, expected: []export.Record{ export.NewRecord( &basicDesc, attribute.EmptySet(), &ocExactAggregator{ points: []aggregation.Point{ { Number: number.NewInt64Number(123), Time: now, }, }, }, now, now, ), }, exportErr: errors.New("failed to export"), }, { desc: "partial success sends correct metrics and drops incorrect metrics with handled err", input: []*metricdata.Metric{ { TimeSeries: []*metricdata.TimeSeries{ { StartTime: now, Points: []metricdata.Point{ {Value: int64(123), Time: now}, }, }, }, }, // TypeGaugeDistribution isn't supported {Descriptor: metricdata.Descriptor{Type: metricdata.TypeGaugeDistribution}}, }, expected: []export.Record{ export.NewRecord( &basicDesc, attribute.EmptySet(), &ocExactAggregator{ points: []aggregation.Point{ { Number: number.NewInt64Number(123), Time: now, }, }, }, now, now, ), }, expectedHandledError: errConversion, }, } { t.Run(tc.desc, func(t *testing.T) { fakeExporter := &fakeExporter{err: tc.exportErr} err := NewMetricExporter(fakeExporter).ExportMetrics(context.Background(), tc.input) if !errors.Is(err, tc.exportErr) { t.Errorf("NewMetricExporter(%+v) = err(%v), want err(%v)", tc.input, err, tc.exportErr) } // Check the global error handler, since we don't return errors // which occur during conversion. err = fakeErrorHandler.matches(tc.expectedHandledError) if err != nil { t.Fatalf("ExportMetrics(%+v) = %v", tc.input, err) } output := fakeExporter.records if len(tc.expected) != len(output) { t.Fatalf("ExportMetrics(%+v) = %d records, want %d records", tc.input, len(output), len(tc.expected)) } if fakeExporter.resource.String() != tc.expectedResource.String() { t.Errorf("ExportMetrics(%+v)[i].Resource() = %+v, want %+v", tc.input, fakeExporter.resource.String(), tc.expectedResource.String()) } for i, expected := range tc.expected { if output[i].StartTime() != expected.StartTime() { t.Errorf("ExportMetrics(%+v)[i].StartTime() = %+v, want %+v", tc.input, output[i].StartTime(), expected.StartTime()) } if output[i].EndTime() != expected.EndTime() { t.Errorf("ExportMetrics(%+v)[i].EndTime() = %+v, want %+v", tc.input, output[i].EndTime(), expected.EndTime()) } if output[i].Descriptor().Name() != expected.Descriptor().Name() { t.Errorf("ExportMetrics(%+v)[i].Descriptor() = %+v, want %+v", tc.input, output[i].Descriptor().Name(), expected.Descriptor().Name()) } // Don't bother with a complete check of the descriptor. // That is checked as part of descriptor conversion tests below. if !output[i].Labels().Equals(expected.Labels()) { t.Errorf("ExportMetrics(%+v)[i].Labels() = %+v, want %+v", tc.input, output[i].Labels(), expected.Labels()) } if output[i].Aggregation().Kind() != expected.Aggregation().Kind() { t.Errorf("ExportMetrics(%+v)[i].Aggregation() = %+v, want %+v", tc.input, output[i].Aggregation().Kind(), expected.Aggregation().Kind()) } // Don't bother checking the contents of the points aggregation. // Those tests are done with the aggregations themselves } }) } } func TestConvertLabels(t *testing.T) { setWithMultipleKeys := attribute.NewSet( attribute.KeyValue{Key: attribute.Key("first"), Value: attribute.StringValue("1")}, attribute.KeyValue{Key: attribute.Key("second"), Value: attribute.StringValue("2")}, ) for _, tc := range []struct { desc string inputKeys []metricdata.LabelKey inputValues []metricdata.LabelValue expected *attribute.Set expectedErr error }{ { desc: "no labels", expected: attribute.EmptySet(), }, { desc: "different numbers of keys and values", inputKeys: []metricdata.LabelKey{{Key: "foo"}}, expected: attribute.EmptySet(), expectedErr: errConversion, }, { desc: "multiple keys and values", inputKeys: []metricdata.LabelKey{{Key: "first"}, {Key: "second"}}, inputValues: []metricdata.LabelValue{ {Value: "1", Present: true}, {Value: "2", Present: true}, }, expected: &setWithMultipleKeys, }, { desc: "multiple keys and values with some not present", inputKeys: []metricdata.LabelKey{{Key: "first"}, {Key: "second"}, {Key: "third"}}, inputValues: []metricdata.LabelValue{ {Value: "1", Present: true}, {Value: "2", Present: true}, {Present: false}, }, expected: &setWithMultipleKeys, }, } { t.Run(tc.desc, func(t *testing.T) { output, err := convertLabels(tc.inputKeys, tc.inputValues) if !errors.Is(err, tc.expectedErr) { t.Errorf("convertLabels(keys: %v, values: %v) = err(%v), want err(%v)", tc.inputKeys, tc.inputValues, err, tc.expectedErr) } if !output.Equals(tc.expected) { t.Errorf("convertLabels(keys: %v, values: %v) = %+v, want %+v", tc.inputKeys, tc.inputValues, output.ToSlice(), tc.expected.ToSlice()) } }) } } func TestConvertResource(t *testing.T) { for _, tc := range []struct { desc string input *ocresource.Resource expected *resource.Resource }{ { desc: "nil resource", }, { desc: "empty resource", input: &ocresource.Resource{ Labels: map[string]string{}, }, expected: resource.NewSchemaless(), }, { desc: "resource with labels", input: &ocresource.Resource{ Labels: map[string]string{ "foo": "bar", "tick": "tock", }, }, expected: resource.NewSchemaless( attribute.KeyValue{Key: attribute.Key("foo"), Value: attribute.StringValue("bar")}, attribute.KeyValue{Key: attribute.Key("tick"), Value: attribute.StringValue("tock")}, ), }, } { t.Run(tc.desc, func(t *testing.T) { output := convertResource(tc.input) if !output.Equal(tc.expected) { t.Errorf("convertResource(%v) = %+v, want %+v", tc.input, output, tc.expected) } }) } } func TestConvertDescriptor(t *testing.T) { for _, tc := range []struct { desc string input metricdata.Descriptor expected sdkapi.Descriptor expectedErr error }{ { desc: "empty descriptor", expected: metrictest.NewDescriptor( "", sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, ), }, { desc: "gauge int64 bytes", input: metricdata.Descriptor{ Name: "foo", Description: "bar", Unit: metricdata.UnitBytes, Type: metricdata.TypeGaugeInt64, }, expected: metrictest.NewDescriptor( "foo", sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, metric.WithDescription("bar"), metric.WithUnit(unit.Bytes), ), }, { desc: "gauge float64 ms", input: metricdata.Descriptor{ Name: "foo", Description: "bar", Unit: metricdata.UnitMilliseconds, Type: metricdata.TypeGaugeFloat64, }, expected: metrictest.NewDescriptor( "foo", sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, metric.WithDescription("bar"), metric.WithUnit(unit.Milliseconds), ), }, { desc: "cumulative int64 dimensionless", input: metricdata.Descriptor{ Name: "foo", Description: "bar", Unit: metricdata.UnitDimensionless, Type: metricdata.TypeCumulativeInt64, }, expected: metrictest.NewDescriptor( "foo", sdkapi.CounterObserverInstrumentKind, number.Int64Kind, metric.WithDescription("bar"), metric.WithUnit(unit.Dimensionless), ), }, { desc: "cumulative float64 dimensionless", input: metricdata.Descriptor{ Name: "foo", Description: "bar", Unit: metricdata.UnitDimensionless, Type: metricdata.TypeCumulativeFloat64, }, expected: metrictest.NewDescriptor( "foo", sdkapi.CounterObserverInstrumentKind, number.Float64Kind, metric.WithDescription("bar"), metric.WithUnit(unit.Dimensionless), ), }, { desc: "incompatible TypeCumulativeDistribution", input: metricdata.Descriptor{ Name: "foo", Description: "bar", Type: metricdata.TypeCumulativeDistribution, }, expectedErr: errConversion, }, } { t.Run(tc.desc, func(t *testing.T) { output, err := convertDescriptor(tc.input) if !errors.Is(err, tc.expectedErr) { t.Errorf("convertDescriptor(%v) = err(%v), want err(%v)", tc.input, err, tc.expectedErr) } if output != tc.expected { t.Errorf("convertDescriptor(%v) = %+v, want %+v", tc.input, output, tc.expected) } }) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/go.mod000066400000000000000000000056301414226744000233470ustar00rootroot00000000000000module go.opentelemetry.io/otel/bridge/opencensus go 1.15 require ( go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/bridge/opencensus => ./ replace go.opentelemetry.io/otel/bridge/opentracing => ../opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ./test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/bridge/opencensus/go.sum000066400000000000000000000141501414226744000233710ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f h1:IUmbcoP9XyEXW+R9AbrZgDvaYVfTbISN92Y5RIV+Mx4= go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/000077500000000000000000000000001414226744000240515ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/handler.go000066400000000000000000000014041414226744000260140ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel" // Handle is the package level function to handle errors. It can be // overwritten for testing. var Handle = otel.Handle golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/000077500000000000000000000000001414226744000254205ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/attributes.go000066400000000000000000000024321414226744000301360ustar00rootroot00000000000000// 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 oc2otel import ( octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" ) func Attributes(attr []octrace.Attribute) []attribute.KeyValue { otelAttr := make([]attribute.KeyValue, len(attr)) for i, a := range attr { otelAttr[i] = attribute.KeyValue{ Key: attribute.Key(a.Key()), Value: AttributeValue(a.Value()), } } return otelAttr } func AttributeValue(ocval interface{}) attribute.Value { switch v := ocval.(type) { case bool: return attribute.BoolValue(v) case int64: return attribute.Int64Value(v) case float64: return attribute.Float64Value(v) case string: return attribute.StringValue(v) default: return attribute.StringValue("unknown") } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/attributes_test.go000066400000000000000000000030341414226744000311740ustar00rootroot00000000000000// 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 oc2otel import ( "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" ) func TestAttributes(t *testing.T) { in := []octrace.Attribute{ octrace.BoolAttribute("bool", true), octrace.Int64Attribute("int64", 49), octrace.Float64Attribute("float64", 1.618), octrace.StringAttribute("key", "val"), } want := []attribute.KeyValue{ attribute.Bool("bool", true), attribute.Int64("int64", 49), attribute.Float64("float64", 1.618), attribute.String("key", "val"), } got := Attributes(in) if len(got) != len(want) { t.Errorf("Attributes conversion failed: want %#v, got %#v", want, got) } for i := range got { if g, w := got[i], want[i]; g != w { t.Errorf("Attributes conversion: want %#v, got %#v", w, g) } } } func TestAttributeValueUnknown(t *testing.T) { got := AttributeValue([]byte{}) if got != attribute.StringValue("unknown") { t.Errorf("AttributeValue of unknown wrong: %#v", got) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/span_context.go000066400000000000000000000017661414226744000304660ustar00rootroot00000000000000// 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 oc2otel import ( octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/trace" ) func SpanContext(sc octrace.SpanContext) trace.SpanContext { var traceFlags trace.TraceFlags if sc.IsSampled() { traceFlags = trace.FlagsSampled } return trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID(sc.TraceID), SpanID: trace.SpanID(sc.SpanID), TraceFlags: traceFlags, }) } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/span_context_test.go000066400000000000000000000043521414226744000315170ustar00rootroot00000000000000// 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 oc2otel import ( "testing" octrace "go.opencensus.io/trace" "go.opencensus.io/trace/tracestate" "go.opentelemetry.io/otel/trace" ) func TestSpanContextConversion(t *testing.T) { for _, tc := range []struct { description string input octrace.SpanContext expected trace.SpanContext }{ { description: "empty", }, { description: "sampled", input: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0x1), }, expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), TraceFlags: trace.FlagsSampled, }), }, { description: "not sampled", input: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0), }, expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), }), }, { description: "trace state is ignored", input: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), Tracestate: &tracestate.Tracestate{}, }, expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), }), }, } { t.Run(tc.description, func(t *testing.T) { output := SpanContext(tc.input) if !output.Equal(tc.expected) { t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected) } }) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/tracer_start_options.go000066400000000000000000000025531414226744000322240ustar00rootroot00000000000000// 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 oc2otel import ( "fmt" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/trace" ) func StartOptions(optFuncs []octrace.StartOption) ([]trace.SpanStartOption, error) { var ocOpts octrace.StartOptions for _, fn := range optFuncs { fn(&ocOpts) } var otelOpts []trace.SpanStartOption switch ocOpts.SpanKind { case octrace.SpanKindClient: otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindClient)) case octrace.SpanKindServer: otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindServer)) case octrace.SpanKindUnspecified: otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindUnspecified)) } var err error if ocOpts.Sampler != nil { err = fmt.Errorf("unsupported sampler: %v", ocOpts.Sampler) } return otelOpts, err } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/oc2otel/tracer_start_options_test.go000066400000000000000000000030441414226744000332570ustar00rootroot00000000000000// 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 oc2otel import ( "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/trace" ) func TestStartOptionsSpanKind(t *testing.T) { conv := map[int]trace.SpanKind{ octrace.SpanKindClient: trace.SpanKindClient, octrace.SpanKindServer: trace.SpanKindServer, octrace.SpanKindUnspecified: trace.SpanKindUnspecified, } for oc, otel := range conv { ocOpts := []octrace.StartOption{octrace.WithSpanKind(oc)} otelOpts, err := StartOptions(ocOpts) if err != nil { t.Errorf("StartOptions errored: %v", err) continue } c := trace.NewSpanStartConfig(otelOpts...) if c.SpanKind() != otel { t.Errorf("conversion of SpanKind start option: got %v, want %v", c.SpanKind(), otel) } } } func TestStartOptionsSamplerErrors(t *testing.T) { ocOpts := []octrace.StartOption{octrace.WithSampler(octrace.AlwaysSample())} _, err := StartOptions(ocOpts) if err == nil { t.Error("StartOptions should error Sampler option") } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/otel2oc/000077500000000000000000000000001414226744000254205ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/otel2oc/span_context.go000066400000000000000000000020101414226744000304450ustar00rootroot00000000000000// 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 otel2oc import ( octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/trace" ) func SpanContext(sc trace.SpanContext) octrace.SpanContext { var to octrace.TraceOptions if sc.IsSampled() { // OpenCensus doesn't expose functions to directly set sampled to = 0x1 } return octrace.SpanContext{ TraceID: octrace.TraceID(sc.TraceID()), SpanID: octrace.SpanID(sc.SpanID()), TraceOptions: to, } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/otel2oc/span_context_test.go000066400000000000000000000035221414226744000315150ustar00rootroot00000000000000// 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 otel2oc import ( "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/trace" ) func TestSpanContextConversion(t *testing.T) { for _, tc := range []struct { description string input trace.SpanContext expected octrace.SpanContext }{ { description: "empty", }, { description: "sampled", input: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), TraceFlags: trace.FlagsSampled, }), expected: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0x1), }, }, { description: "not sampled", input: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), }), expected: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0), }, }, } { t.Run(tc.description, func(t *testing.T) { output := SpanContext(tc.input) if output != tc.expected { t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected) } }) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/span.go000066400000000000000000000102511414226744000253400ustar00rootroot00000000000000// 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 ( "fmt" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) const ( // MessageSendEvent is the name of the message send event. MessageSendEvent = "message send" // MessageReceiveEvent is the name of the message receive event. MessageReceiveEvent = "message receive" ) var ( // UncompressedKey is used for the uncompressed byte size attribute. UncompressedKey = attribute.Key("uncompressed byte size") // CompressedKey is used for the compressed byte size attribute. CompressedKey = attribute.Key("compressed byte size") ) // Span is an OpenCensus SpanInterface wrapper for an OpenTelemetry Span. type Span struct { otelSpan trace.Span } // NewSpan returns an OpenCensus Span wrapping an OpenTelemetry Span. func NewSpan(s trace.Span) *octrace.Span { return octrace.NewSpan(&Span{otelSpan: s}) } // IsRecordingEvents returns true if events are being recorded for this span. func (s *Span) IsRecordingEvents() bool { return s.otelSpan.IsRecording() } // End ends thi span. func (s *Span) End() { s.otelSpan.End() } // SpanContext returns the SpanContext of this span. func (s *Span) SpanContext() octrace.SpanContext { return otel2oc.SpanContext(s.otelSpan.SpanContext()) } // SetName sets the name of this span, if it is recording events. func (s *Span) SetName(name string) { s.otelSpan.SetName(name) } // SetStatus sets the status of this span, if it is recording events. func (s *Span) SetStatus(status octrace.Status) { s.otelSpan.SetStatus(codes.Code(status.Code), status.Message) } // AddAttributes sets attributes in this span. func (s *Span) AddAttributes(attributes ...octrace.Attribute) { s.otelSpan.SetAttributes(oc2otel.Attributes(attributes)...) } // Annotate adds an annotation with attributes to this span. func (s *Span) Annotate(attributes []octrace.Attribute, str string) { s.otelSpan.AddEvent(str, trace.WithAttributes(oc2otel.Attributes(attributes)...)) } // Annotatef adds a formatted annotation with attributes to this span. func (s *Span) Annotatef(attributes []octrace.Attribute, format string, a ...interface{}) { s.Annotate(attributes, fmt.Sprintf(format, a...)) } // AddMessageSendEvent adds a message send event to this span. func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { s.otelSpan.AddEvent(MessageSendEvent, trace.WithAttributes( attribute.KeyValue{ Key: UncompressedKey, Value: attribute.Int64Value(uncompressedByteSize), }, attribute.KeyValue{ Key: CompressedKey, Value: attribute.Int64Value(compressedByteSize), }), ) } // AddMessageReceiveEvent adds a message receive event to this span. func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { s.otelSpan.AddEvent(MessageReceiveEvent, trace.WithAttributes( attribute.KeyValue{ Key: UncompressedKey, Value: attribute.Int64Value(uncompressedByteSize), }, attribute.KeyValue{ Key: CompressedKey, Value: attribute.Int64Value(compressedByteSize), }), ) } // AddLink adds a link to this span. func (s *Span) AddLink(l octrace.Link) { Handle(fmt.Errorf("ignoring OpenCensus link %+v for span %q because OpenTelemetry doesn't support setting links after creation", l, s.String())) } // String prints a string representation of this span. func (s *Span) String() string { return fmt.Sprintf("span %s", s.otelSpan.SpanContext().SpanID().String()) } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/span_test.go000066400000000000000000000167071414226744000264130ustar00rootroot00000000000000// 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_test import ( "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/bridge/opencensus/internal" "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) type span struct { trace.Span recording bool ended bool sc trace.SpanContext name string sCode codes.Code sMsg string attrs []attribute.KeyValue eName string eOpts []trace.EventOption } func (s *span) IsRecording() bool { return s.recording } func (s *span) End(...trace.SpanEndOption) { s.ended = true } func (s *span) SpanContext() trace.SpanContext { return s.sc } func (s *span) SetName(n string) { s.name = n } func (s *span) SetStatus(c codes.Code, d string) { s.sCode, s.sMsg = c, d } func (s *span) SetAttributes(a ...attribute.KeyValue) { s.attrs = a } func (s *span) AddEvent(n string, o ...trace.EventOption) { s.eName, s.eOpts = n, o } func TestSpanIsRecordingEvents(t *testing.T) { s := &span{recording: true} ocS := internal.NewSpan(s) if !ocS.IsRecordingEvents() { t.Errorf("span.IsRecordingEvents() = false, want true") } s.recording = false if ocS.IsRecordingEvents() { t.Errorf("span.IsRecordingEvents() = true, want false") } } func TestSpanEnd(t *testing.T) { s := new(span) ocS := internal.NewSpan(s) if s.ended { t.Fatal("new span already ended") } ocS.End() if !s.ended { t.Error("span.End() did not end OpenTelemetry span") } } func TestSpanSpanContext(t *testing.T) { sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: [16]byte{1}, SpanID: [8]byte{1}, }) // Do not test the conversion, only that the method is called. converted := otel2oc.SpanContext(sc) s := &span{sc: sc} ocS := internal.NewSpan(s) if ocS.SpanContext() != converted { t.Error("span.SpanContext did not use OpenTelemetry SpanContext") } } func TestSpanSetName(t *testing.T) { // OpenCensus does not set a name if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) name := "test name" ocS.SetName(name) if s.name != name { t.Error("span.SetName did not set OpenTelemetry span name") } } func TestSpanSetStatus(t *testing.T) { // OpenCensus does not set a status if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) c, d := codes.Error, "error" status := octrace.Status{Code: int32(c), Message: d} ocS.SetStatus(status) if s.sCode != c { t.Error("span.SetStatus failed to set OpenTelemetry status code") } if s.sMsg != d { t.Error("span.SetStatus failed to set OpenTelemetry status description") } } func TestSpanAddAttributes(t *testing.T) { attrs := []octrace.Attribute{ octrace.BoolAttribute("a", true), } // Do not test the conversion, only that the method is called. converted := oc2otel.Attributes(attrs) // OpenCensus does not set attributes if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.AddAttributes(attrs...) if len(s.attrs) != len(converted) || s.attrs[0] != converted[0] { t.Error("span.AddAttributes failed to set OpenTelemetry attributes") } } func TestSpanAnnotate(t *testing.T) { name := "annotation" attrs := []octrace.Attribute{ octrace.BoolAttribute("a", true), } // Do not test the conversion, only that the method is called. want := oc2otel.Attributes(attrs) // OpenCensus does not set events if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.Annotate(attrs, name) if s.eName != name { t.Error("span.Annotate did not set event name") } config := trace.NewEventConfig(s.eOpts...) got := config.Attributes() if len(want) != len(got) || want[0] != got[0] { t.Error("span.Annotate did not set event options") } } func TestSpanAnnotatef(t *testing.T) { format := "annotation %s" attrs := []octrace.Attribute{ octrace.BoolAttribute("a", true), } // Do not test the conversion, only that the method is called. want := oc2otel.Attributes(attrs) // OpenCensus does not set events if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.Annotatef(attrs, format, "a") if s.eName != "annotation a" { t.Error("span.Annotatef did not set event name") } config := trace.NewEventConfig(s.eOpts...) got := config.Attributes() if len(want) != len(got) || want[0] != got[0] { t.Error("span.Annotatef did not set event options") } } func TestSpanAddMessageSendEvent(t *testing.T) { var u, c int64 = 1, 2 // OpenCensus does not set events if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.AddMessageSendEvent(0, u, c) if s.eName != internal.MessageSendEvent { t.Error("span.AddMessageSendEvent did not set event name") } config := trace.NewEventConfig(s.eOpts...) got := config.Attributes() if len(got) != 2 { t.Fatalf("span.AddMessageSendEvent set %d attributes, want 2", len(got)) } want := attribute.KeyValue{Key: internal.UncompressedKey, Value: attribute.Int64Value(u)} if got[0] != want { t.Errorf("span.AddMessageSendEvent wrong uncompressed attribute: %v", got[0]) } want = attribute.KeyValue{Key: internal.CompressedKey, Value: attribute.Int64Value(c)} if got[1] != want { t.Errorf("span.AddMessageSendEvent wrong compressed attribute: %v", got[1]) } } func TestSpanAddMessageReceiveEvent(t *testing.T) { var u, c int64 = 3, 4 // OpenCensus does not set events if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.AddMessageReceiveEvent(0, u, c) if s.eName != internal.MessageReceiveEvent { t.Error("span.AddMessageReceiveEvent did not set event name") } config := trace.NewEventConfig(s.eOpts...) got := config.Attributes() if len(got) != 2 { t.Fatalf("span.AddMessageReceiveEvent set %d attributes, want 2", len(got)) } want := attribute.KeyValue{Key: internal.UncompressedKey, Value: attribute.Int64Value(u)} if got[0] != want { t.Errorf("span.AddMessageReceiveEvent wrong uncompressed attribute: %v", got[0]) } want = attribute.KeyValue{Key: internal.CompressedKey, Value: attribute.Int64Value(c)} if got[1] != want { t.Errorf("span.AddMessageReceiveEvent wrong compressed attribute: %v", got[1]) } } func TestSpanAddLinkFails(t *testing.T) { h, restore := withHandler() defer restore() // OpenCensus does not try to set links if not recording. s := &span{recording: true} ocS := internal.NewSpan(s) ocS.AddLink(octrace.Link{}) if h.err == nil { t.Error("span.AddLink failed to raise an error") } } func TestSpanString(t *testing.T) { sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: [16]byte{1}, SpanID: [8]byte{1}, }) s := &span{sc: sc} ocS := internal.NewSpan(s) if expected := "span 0100000000000000"; ocS.String() != expected { t.Errorf("span.String = %q, not %q", ocS.String(), expected) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/tracer.go000066400000000000000000000050221414226744000256570ustar00rootroot00000000000000// 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 ( "context" "fmt" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" "go.opentelemetry.io/otel/trace" ) // Tracer is an OpenCensus Tracer that wraps an OpenTelemetry Tracer. type Tracer struct { otelTracer trace.Tracer } // NewTracer returns an OpenCensus Tracer that wraps the OpenTelemetry tracer. func NewTracer(tracer trace.Tracer) octrace.Tracer { return &Tracer{otelTracer: tracer} } // StartSpan starts a new child span of the current span in the context. If // there is no span in the context, it creates a new trace and span. func (o *Tracer) StartSpan(ctx context.Context, name string, s ...octrace.StartOption) (context.Context, *octrace.Span) { otelOpts, err := oc2otel.StartOptions(s) if err != nil { Handle(fmt.Errorf("starting span %q: %w", name, err)) } ctx, sp := o.otelTracer.Start(ctx, name, otelOpts...) return ctx, NewSpan(sp) } // StartSpanWithRemoteParent starts a new child span of the span from the // given parent. func (o *Tracer) StartSpanWithRemoteParent(ctx context.Context, name string, parent octrace.SpanContext, s ...octrace.StartOption) (context.Context, *octrace.Span) { // make sure span context is zero'd out so we use the remote parent ctx = trace.ContextWithSpan(ctx, nil) ctx = trace.ContextWithRemoteSpanContext(ctx, oc2otel.SpanContext(parent)) return o.StartSpan(ctx, name, s...) } // FromContext returns the Span stored in a context. func (o *Tracer) FromContext(ctx context.Context) *octrace.Span { return NewSpan(trace.SpanFromContext(ctx)) } // NewContext returns a new context with the given Span attached. func (o *Tracer) NewContext(parent context.Context, s *octrace.Span) context.Context { if otSpan, ok := s.Internal().(*Span); ok { return trace.ContextWithSpan(parent, otSpan.otelSpan) } Handle(fmt.Errorf("unable to create context with span %q, since it was created using a different tracer", s.String())) return parent } golang-opentelemetry-otel-1.1.0/bridge/opencensus/internal/tracer_test.go000066400000000000000000000113051414226744000267170ustar00rootroot00000000000000// 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_test import ( "context" "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/bridge/opencensus/internal" "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" "go.opentelemetry.io/otel/trace" ) type handler struct{ err error } func (h *handler) Handle(e error) { h.err = e } func withHandler() (*handler, func()) { h := new(handler) original := internal.Handle internal.Handle = h.Handle return h, func() { internal.Handle = original } } type tracer struct { ctx context.Context name string opts []trace.SpanStartOption } func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { t.ctx, t.name, t.opts = ctx, name, opts noop := trace.NewNoopTracerProvider().Tracer("testing") return noop.Start(ctx, name, opts...) } type ctxKey string func TestTracerStartSpan(t *testing.T) { h, restore := withHandler() defer restore() otelTracer := &tracer{} ocTracer := internal.NewTracer(otelTracer) ctx := context.WithValue(context.Background(), ctxKey("key"), "value") name := "testing span" ocTracer.StartSpan(ctx, name, octrace.WithSpanKind(octrace.SpanKindClient)) if h.err != nil { t.Fatalf("OC tracer.StartSpan errored: %v", h.err) } if otelTracer.ctx != ctx { t.Error("OTel tracer.Start called with wrong context") } if otelTracer.name != name { t.Error("OTel tracer.Start called with wrong name") } sk := trace.SpanKindClient c := trace.NewSpanStartConfig(otelTracer.opts...) if c.SpanKind() != sk { t.Errorf("OTel tracer.Start called with wrong options: %#v", c) } } func TestTracerStartSpanReportsErrors(t *testing.T) { h, restore := withHandler() defer restore() ocTracer := internal.NewTracer(&tracer{}) ocTracer.StartSpan(context.Background(), "", octrace.WithSampler(octrace.AlwaysSample())) if h.err == nil { t.Error("OC tracer.StartSpan no error when converting Sampler") } } func TestTracerStartSpanWithRemoteParent(t *testing.T) { otelTracer := new(tracer) ocTracer := internal.NewTracer(otelTracer) sc := octrace.SpanContext{TraceID: [16]byte{1}, SpanID: [8]byte{1}} converted := oc2otel.SpanContext(sc).WithRemote(true) ocTracer.StartSpanWithRemoteParent(context.Background(), "", sc) got := trace.SpanContextFromContext(otelTracer.ctx) if !got.Equal(converted) { t.Error("tracer.StartSpanWithRemoteParent failed to set remote parent") } } func TestTracerFromContext(t *testing.T) { sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: [16]byte{1}, SpanID: [8]byte{1}, }) ctx := trace.ContextWithSpanContext(context.Background(), sc) noop := trace.NewNoopTracerProvider().Tracer("TestTracerFromContext") // Test using the fact that the No-Op span will propagate a span context . ctx, _ = noop.Start(ctx, "test") got := internal.NewTracer(noop).FromContext(ctx).SpanContext() // Do not test the convedsion, only that the propagtion. want := otel2oc.SpanContext(sc) if got != want { t.Errorf("tracer.FromContext returned wrong context: %#v", got) } } func TestTracerNewContext(t *testing.T) { sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: [16]byte{1}, SpanID: [8]byte{1}, }) ctx := trace.ContextWithSpanContext(context.Background(), sc) noop := trace.NewNoopTracerProvider().Tracer("TestTracerNewContext") // Test using the fact that the No-Op span will propagate a span context . _, s := noop.Start(ctx, "test") ocTracer := internal.NewTracer(noop) ctx = ocTracer.NewContext(context.Background(), internal.NewSpan(s)) got := trace.SpanContextFromContext(ctx) if !got.Equal(sc) { t.Error("tracer.NewContext did not attach Span to context") } } type differentSpan struct { octrace.SpanInterface } func (s *differentSpan) String() string { return "testing span" } func TestTracerNewContextErrors(t *testing.T) { h, restore := withHandler() defer restore() ocTracer := internal.NewTracer(&tracer{}) ocSpan := octrace.NewSpan(&differentSpan{}) ocTracer.NewContext(context.Background(), ocSpan) if h.err == nil { t.Error("tracer.NewContext did not error for unrecognized span") } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/test/000077500000000000000000000000001414226744000232145ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opencensus/test/bridge_test.go000066400000000000000000000224371414226744000260460ustar00rootroot00000000000000// 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 test import ( "context" "testing" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" ocbridge "go.opentelemetry.io/otel/bridge/opencensus" "go.opentelemetry.io/otel/bridge/opencensus/internal" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) func TestMixedAPIs(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) tracer := tp.Tracer("mixedapitracer") octrace.DefaultTracer = ocbridge.NewTracer(tracer) func() { ctx := context.Background() var ocspan1 *octrace.Span ctx, ocspan1 = octrace.StartSpan(ctx, "OpenCensusSpan1") defer ocspan1.End() var otspan1 trace.Span ctx, otspan1 = tracer.Start(ctx, "OpenTelemetrySpan1") defer otspan1.End() var ocspan2 *octrace.Span ctx, ocspan2 = octrace.StartSpan(ctx, "OpenCensusSpan2") defer ocspan2.End() var otspan2 trace.Span _, otspan2 = tracer.Start(ctx, "OpenTelemetrySpan2") defer otspan2.End() }() spans := sr.Ended() if len(spans) != 4 { for _, span := range spans { t.Logf("Span: %s", span.Name()) } t.Fatalf("Got %d spans, exepected %d.", len(spans), 4) } var parent trace.SpanContext for i := len(spans) - 1; i >= 0; i-- { // Verify that OpenCensus spans and OpenTelemetry spans have each // other as parents. if psid := spans[i].Parent().SpanID(); psid != parent.SpanID() { t.Errorf("Span %v had parent %v. Expected %v", spans[i].Name(), psid, parent.SpanID()) } parent = spans[i].SpanContext() } } func TestStartOptions(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) octrace.DefaultTracer = ocbridge.NewTracer(tp.Tracer("startoptionstracer")) ctx := context.Background() _, span := octrace.StartSpan(ctx, "OpenCensusSpan", octrace.WithSpanKind(octrace.SpanKindClient)) span.End() spans := sr.Ended() if len(spans) != 1 { t.Fatalf("Got %d spans, exepected %d", len(spans), 1) } if spans[0].SpanKind() != trace.SpanKindClient { t.Errorf("Got span kind %v, exepected %d", spans[0].SpanKind(), trace.SpanKindClient) } } func TestStartSpanWithRemoteParent(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) tracer := tp.Tracer("remoteparent") octrace.DefaultTracer = ocbridge.NewTracer(tracer) ctx := context.Background() ctx, parent := tracer.Start(ctx, "OpenTelemetrySpan1") _, span := octrace.StartSpanWithRemoteParent(ctx, "OpenCensusSpan", ocbridge.OTelSpanContextToOC(parent.SpanContext())) span.End() spans := sr.Ended() if len(spans) != 1 { t.Fatalf("Got %d spans, exepected %d", len(spans), 1) } if psid := spans[0].Parent().SpanID(); psid != parent.SpanContext().SpanID() { t.Errorf("Span %v, had parent %v. Expected %d", spans[0].Name(), psid, parent.SpanContext().SpanID()) } } func TestToFromContext(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) tracer := tp.Tracer("tofromcontext") octrace.DefaultTracer = ocbridge.NewTracer(tracer) func() { ctx := context.Background() _, otSpan1 := tracer.Start(ctx, "OpenTelemetrySpan1") defer otSpan1.End() // Use NewContext instead of the context from Start ctx = octrace.NewContext(ctx, internal.NewSpan(otSpan1)) ctx, _ = tracer.Start(ctx, "OpenTelemetrySpan2") // Get the opentelemetry span using the OpenCensus FromContext, and end it otSpan2 := octrace.FromContext(ctx) defer otSpan2.End() }() spans := sr.Ended() if len(spans) != 2 { t.Fatalf("Got %d spans, exepected %d.", len(spans), 2) } var parent trace.SpanContext for i := len(spans) - 1; i >= 0; i-- { // Verify that OpenCensus spans and OpenTelemetry spans have each // other as parents. if psid := spans[i].Parent().SpanID(); psid != parent.SpanID() { t.Errorf("Span %v had parent %v. Expected %v", spans[i].Name(), psid, parent.SpanID()) } parent = spans[i].SpanContext() } } func TestIsRecordingEvents(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) octrace.DefaultTracer = ocbridge.NewTracer(tp.Tracer("isrecordingevents")) ctx := context.Background() _, ocspan := octrace.StartSpan(ctx, "OpenCensusSpan1") if !ocspan.IsRecordingEvents() { t.Errorf("Got %v, expected true", ocspan.IsRecordingEvents()) } } func attrsMap(s []attribute.KeyValue) map[attribute.Key]attribute.Value { m := make(map[attribute.Key]attribute.Value, len(s)) for _, a := range s { m[a.Key] = a.Value } return m } func TestSetThings(t *testing.T) { sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) octrace.DefaultTracer = ocbridge.NewTracer(tp.Tracer("setthings")) ctx := context.Background() _, ocspan := octrace.StartSpan(ctx, "OpenCensusSpan1") ocspan.SetName("span-foo") ocspan.SetStatus(octrace.Status{Code: 1, Message: "foo"}) ocspan.AddAttributes( octrace.BoolAttribute("bool", true), octrace.Int64Attribute("int64", 12345), octrace.Float64Attribute("float64", 12.345), octrace.StringAttribute("string", "stringval"), ) ocspan.Annotate( []octrace.Attribute{octrace.StringAttribute("string", "annotateval")}, "annotate", ) ocspan.Annotatef( []octrace.Attribute{ octrace.Int64Attribute("int64", 12345), octrace.Float64Attribute("float64", 12.345), }, "annotate%d", 67890, ) ocspan.AddMessageSendEvent(123, 456, 789) ocspan.AddMessageReceiveEvent(246, 135, 369) ocspan.End() spans := sr.Ended() if len(spans) != 1 { t.Fatalf("Got %d spans, exepected %d.", len(spans), 1) } s := spans[0] if s.Name() != "span-foo" { t.Errorf("Got name %v, expected span-foo", s.Name()) } if s.Status().Code != codes.Error { t.Errorf("Got code %v, expected %v", s.Status().Code, codes.Error) } if s.Status().Description != "foo" { t.Errorf("Got code %v, expected foo", s.Status().Description) } attrs := attrsMap(s.Attributes()) if v := attrs[attribute.Key("bool")]; !v.AsBool() { t.Errorf("Got attributes[bool] %v, expected true", v.AsBool()) } if v := attrs[attribute.Key("int64")]; v.AsInt64() != 12345 { t.Errorf("Got attributes[int64] %v, expected 12345", v.AsInt64()) } if v := attrs[attribute.Key("float64")]; v.AsFloat64() != 12.345 { t.Errorf("Got attributes[float64] %v, expected 12.345", v.AsFloat64()) } if v := attrs[attribute.Key("string")]; v.AsString() != "stringval" { t.Errorf("Got attributes[string] %v, expected stringval", v.AsString()) } if len(s.Events()) != 4 { t.Fatalf("Got len(events) = %v, expected 4", len(s.Events())) } annotateEvent := s.Events()[0] aeAttrs := attrsMap(annotateEvent.Attributes) annotatefEvent := s.Events()[1] afeAttrs := attrsMap(annotatefEvent.Attributes) sendEvent := s.Events()[2] receiveEvent := s.Events()[3] if v := aeAttrs[attribute.Key("string")]; v.AsString() != "annotateval" { t.Errorf("Got annotateEvent.Attributes[string] = %v, expected annotateval", v.AsString()) } if annotateEvent.Name != "annotate" { t.Errorf("Got annotateEvent.Name = %v, expected annotate", annotateEvent.Name) } if v := afeAttrs[attribute.Key("int64")]; v.AsInt64() != 12345 { t.Errorf("Got annotatefEvent.Attributes[int64] = %v, expected 12345", v.AsInt64()) } if v := afeAttrs[attribute.Key("float64")]; v.AsFloat64() != 12.345 { t.Errorf("Got annotatefEvent.Attributes[float64] = %v, expected 12.345", v.AsFloat64()) } if annotatefEvent.Name != "annotate67890" { t.Errorf("Got annotatefEvent.Name = %v, expected annotate67890", annotatefEvent.Name) } if v := aeAttrs[attribute.Key("string")]; v.AsString() != "annotateval" { t.Errorf("Got annotateEvent.Attributes[string] = %v, expected annotateval", v.AsString()) } seAttrs := attrsMap(sendEvent.Attributes) reAttrs := attrsMap(receiveEvent.Attributes) if sendEvent.Name != internal.MessageSendEvent { t.Errorf("Got sendEvent.Name = %v, expected message send", sendEvent.Name) } if v := seAttrs[internal.UncompressedKey]; v.AsInt64() != 456 { t.Errorf("Got sendEvent.Attributes[uncompressedKey] = %v, expected 456", v.AsInt64()) } if v := seAttrs[internal.CompressedKey]; v.AsInt64() != 789 { t.Errorf("Got sendEvent.Attributes[compressedKey] = %v, expected 789", v.AsInt64()) } if receiveEvent.Name != internal.MessageReceiveEvent { t.Errorf("Got receiveEvent.Name = %v, expected message receive", receiveEvent.Name) } if v := reAttrs[internal.UncompressedKey]; v.AsInt64() != 135 { t.Errorf("Got receiveEvent.Attributes[uncompressedKey] = %v, expected 135", v.AsInt64()) } if v := reAttrs[internal.CompressedKey]; v.AsInt64() != 369 { t.Errorf("Got receiveEvent.Attributes[compressedKey] = %v, expected 369", v.AsInt64()) } } golang-opentelemetry-otel-1.1.0/bridge/opencensus/test/go.mod000066400000000000000000000054441414226744000243310ustar00rootroot00000000000000module go.opentelemetry.io/otel/bridge/opencensus/test go 1.15 require ( go.opencensus.io v0.23.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/bridge/opencensus v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel => ../../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ./ replace go.opentelemetry.io/otel/bridge/opentracing => ../../opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/jaeger => ../../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/prometheus => ../../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/zipkin => ../../../exporters/zipkin replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/sdk => ../../../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/bridge/opencensus/test/go.sum000066400000000000000000000253711414226744000243570ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/bridge/opentracing/000077500000000000000000000000001414226744000223645ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opentracing/bridge.go000066400000000000000000000461371414226744000241620ustar00rootroot00000000000000// 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 opentracing // import "go.opentelemetry.io/otel/bridge/opentracing" import ( "context" "fmt" "net/http" "strings" "sync" ot "github.com/opentracing/opentracing-go" otext "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/bridge/opentracing/migration" "go.opentelemetry.io/otel/codes" iBaggage "go.opentelemetry.io/otel/internal/baggage" "go.opentelemetry.io/otel/internal/trace/noop" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) type bridgeSpanContext struct { bag baggage.Baggage otelSpanContext trace.SpanContext } var _ ot.SpanContext = &bridgeSpanContext{} func newBridgeSpanContext(otelSpanContext trace.SpanContext, parentOtSpanContext ot.SpanContext) *bridgeSpanContext { bCtx := &bridgeSpanContext{ bag: baggage.Baggage{}, otelSpanContext: otelSpanContext, } if parentOtSpanContext != nil { parentOtSpanContext.ForeachBaggageItem(func(key, value string) bool { bCtx.setBaggageItem(key, value) return true }) } return bCtx } func (c *bridgeSpanContext) ForeachBaggageItem(handler func(k, v string) bool) { for _, m := range c.bag.Members() { if !handler(m.Key(), m.Value()) { return } } } func (c *bridgeSpanContext) setBaggageItem(restrictedKey, value string) { crk := http.CanonicalHeaderKey(restrictedKey) m, err := baggage.NewMember(crk, value) if err != nil { return } c.bag, _ = c.bag.SetMember(m) } func (c *bridgeSpanContext) baggageItem(restrictedKey string) baggage.Member { crk := http.CanonicalHeaderKey(restrictedKey) return c.bag.Member(crk) } type bridgeSpan struct { otelSpan trace.Span ctx *bridgeSpanContext tracer *BridgeTracer skipDeferHook bool extraBaggageItems map[string]string } var _ ot.Span = &bridgeSpan{} func newBridgeSpan(otelSpan trace.Span, bridgeSC *bridgeSpanContext, tracer *BridgeTracer) *bridgeSpan { return &bridgeSpan{ otelSpan: otelSpan, ctx: bridgeSC, tracer: tracer, skipDeferHook: false, extraBaggageItems: nil, } } func (s *bridgeSpan) Finish() { s.otelSpan.End() } func (s *bridgeSpan) FinishWithOptions(opts ot.FinishOptions) { var otelOpts []trace.SpanEndOption if !opts.FinishTime.IsZero() { otelOpts = append(otelOpts, trace.WithTimestamp(opts.FinishTime)) } for _, record := range opts.LogRecords { s.logRecord(record) } for _, data := range opts.BulkLogData { s.logRecord(data.ToLogRecord()) } s.otelSpan.End(otelOpts...) } func (s *bridgeSpan) logRecord(record ot.LogRecord) { s.otelSpan.AddEvent( "", trace.WithTimestamp(record.Timestamp), trace.WithAttributes(otLogFieldsToOTelLabels(record.Fields)...), ) } func (s *bridgeSpan) Context() ot.SpanContext { return s.ctx } func (s *bridgeSpan) SetOperationName(operationName string) ot.Span { s.otelSpan.SetName(operationName) return s } // SetTag method adds a tag to the span. // // Note about the following value conversions: // - int -> int64 // - uint -> string // - int32 -> int64 // - uint32 -> int64 // - uint64 -> string // - float32 -> float64 func (s *bridgeSpan) SetTag(key string, value interface{}) ot.Span { switch key { case string(otext.SpanKind): // TODO: Should we ignore it? case string(otext.Error): if b, ok := value.(bool); ok && b { s.otelSpan.SetStatus(codes.Error, "") } default: s.otelSpan.SetAttributes(otTagToOTelLabel(key, value)) } return s } func (s *bridgeSpan) LogFields(fields ...otlog.Field) { s.otelSpan.AddEvent( "", trace.WithAttributes(otLogFieldsToOTelLabels(fields)...), ) } type bridgeFieldEncoder struct { pairs []attribute.KeyValue } var _ otlog.Encoder = &bridgeFieldEncoder{} func (e *bridgeFieldEncoder) EmitString(key, value string) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitBool(key string, value bool) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitInt(key string, value int) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitInt32(key string, value int32) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitInt64(key string, value int64) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitUint32(key string, value uint32) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitUint64(key string, value uint64) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitFloat32(key string, value float32) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitFloat64(key string, value float64) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitObject(key string, value interface{}) { e.emitCommon(key, value) } func (e *bridgeFieldEncoder) EmitLazyLogger(value otlog.LazyLogger) { value(e) } func (e *bridgeFieldEncoder) emitCommon(key string, value interface{}) { e.pairs = append(e.pairs, otTagToOTelLabel(key, value)) } func otLogFieldsToOTelLabels(fields []otlog.Field) []attribute.KeyValue { encoder := &bridgeFieldEncoder{} for _, field := range fields { field.Marshal(encoder) } return encoder.pairs } func (s *bridgeSpan) LogKV(alternatingKeyValues ...interface{}) { fields, err := otlog.InterleavedKVToFields(alternatingKeyValues...) if err != nil { return } s.LogFields(fields...) } func (s *bridgeSpan) SetBaggageItem(restrictedKey, value string) ot.Span { s.updateOTelContext(restrictedKey, value) s.setBaggageItemOnly(restrictedKey, value) return s } func (s *bridgeSpan) setBaggageItemOnly(restrictedKey, value string) { s.ctx.setBaggageItem(restrictedKey, value) } func (s *bridgeSpan) updateOTelContext(restrictedKey, value string) { if s.extraBaggageItems == nil { s.extraBaggageItems = make(map[string]string) } s.extraBaggageItems[restrictedKey] = value } func (s *bridgeSpan) BaggageItem(restrictedKey string) string { return s.ctx.baggageItem(restrictedKey).Value() } func (s *bridgeSpan) Tracer() ot.Tracer { return s.tracer } func (s *bridgeSpan) LogEvent(event string) { s.LogEventWithPayload(event, nil) } func (s *bridgeSpan) LogEventWithPayload(event string, payload interface{}) { data := ot.LogData{ Event: event, Payload: payload, } s.Log(data) } func (s *bridgeSpan) Log(data ot.LogData) { record := data.ToLogRecord() s.LogFields(record.Fields...) } type bridgeSetTracer struct { isSet bool otelTracer trace.Tracer warningHandler BridgeWarningHandler warnOnce sync.Once } func (s *bridgeSetTracer) tracer() trace.Tracer { if !s.isSet { s.warnOnce.Do(func() { s.warningHandler("The OpenTelemetry tracer is not set, default no-op tracer is used! Call SetOpenTelemetryTracer to set it up.\n") }) } return s.otelTracer } // BridgeWarningHandler is a type of handler that receives warnings // from the BridgeTracer. type BridgeWarningHandler func(msg string) // BridgeTracer is an implementation of the OpenTracing tracer, which // translates the calls to the OpenTracing API into OpenTelemetry // counterparts and calls the underlying OpenTelemetry tracer. type BridgeTracer struct { setTracer bridgeSetTracer warningHandler BridgeWarningHandler warnOnce sync.Once propagator propagation.TextMapPropagator } var _ ot.Tracer = &BridgeTracer{} var _ ot.TracerContextWithSpanExtension = &BridgeTracer{} // NewBridgeTracer creates a new BridgeTracer. The new tracer forwards // the calls to the OpenTelemetry Noop tracer, so it should be // overridden with the SetOpenTelemetryTracer function. The warnings // handler does nothing by default, so to override it use the // SetWarningHandler function. func NewBridgeTracer() *BridgeTracer { return &BridgeTracer{ setTracer: bridgeSetTracer{ otelTracer: noop.Tracer, }, warningHandler: func(msg string) {}, propagator: nil, } } // SetWarningHandler overrides the warning handler. func (t *BridgeTracer) SetWarningHandler(handler BridgeWarningHandler) { t.setTracer.warningHandler = handler t.warningHandler = handler } // SetOpenTelemetryTracer overrides the underlying OpenTelemetry // tracer. The passed tracer should know how to operate in the // environment that uses OpenTracing API. func (t *BridgeTracer) SetOpenTelemetryTracer(tracer trace.Tracer) { t.setTracer.otelTracer = tracer t.setTracer.isSet = true } func (t *BridgeTracer) SetTextMapPropagator(propagator propagation.TextMapPropagator) { t.propagator = propagator } func (t *BridgeTracer) NewHookedContext(ctx context.Context) context.Context { ctx = iBaggage.ContextWithSetHook(ctx, t.baggageSetHook) ctx = iBaggage.ContextWithGetHook(ctx, t.baggageGetHook) return ctx } func (t *BridgeTracer) baggageSetHook(ctx context.Context, list iBaggage.List) context.Context { span := ot.SpanFromContext(ctx) if span == nil { t.warningHandler("No active OpenTracing span, can not propagate the baggage items from OpenTelemetry context\n") return ctx } bSpan, ok := span.(*bridgeSpan) if !ok { t.warningHandler("Encountered a foreign OpenTracing span, will not propagate the baggage items from OpenTelemetry context\n") return ctx } for k, v := range list { bSpan.setBaggageItemOnly(k, v.Value) } return ctx } func (t *BridgeTracer) baggageGetHook(ctx context.Context, list iBaggage.List) iBaggage.List { span := ot.SpanFromContext(ctx) if span == nil { t.warningHandler("No active OpenTracing span, can not propagate the baggage items from OpenTracing span context\n") return list } bSpan, ok := span.(*bridgeSpan) if !ok { t.warningHandler("Encountered a foreign OpenTracing span, will not propagate the baggage items from OpenTracing span context\n") return list } items := bSpan.extraBaggageItems if len(items) == 0 { return list } // Privilege of using the internal representation of Baggage here comes // with the responsibility to make sure we maintain its immutability. We // need to return a copy to ensure this. merged := make(iBaggage.List, len(list)) for k, v := range list { merged[k] = v } for k, v := range items { // Overwrite according to OpenTelemetry specification. merged[k] = iBaggage.Item{Value: v} } return merged } // StartSpan is a part of the implementation of the OpenTracing Tracer // interface. func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span { sso := ot.StartSpanOptions{} for _, opt := range opts { opt.Apply(&sso) } parentBridgeSC, links := otSpanReferencesToParentAndLinks(sso.References) attributes, kind, hadTrueErrorTag := otTagsToOTelAttributesKindAndError(sso.Tags) checkCtx := migration.WithDeferredSetup(context.Background()) if parentBridgeSC != nil { checkCtx = trace.ContextWithRemoteSpanContext(checkCtx, parentBridgeSC.otelSpanContext) } checkCtx2, otelSpan := t.setTracer.tracer().Start( checkCtx, operationName, trace.WithAttributes(attributes...), trace.WithTimestamp(sso.StartTime), trace.WithLinks(links...), trace.WithSpanKind(kind), ) if checkCtx != checkCtx2 { t.warnOnce.Do(func() { t.warningHandler("SDK should have deferred the context setup, see the documentation of go.opentelemetry.io/otel/bridge/opentracing/migration\n") }) } if hadTrueErrorTag { otelSpan.SetStatus(codes.Error, "") } // One does not simply pass a concrete pointer to function // that takes some interface. In case of passing nil concrete // pointer, we get an interface with non-nil type (because the // pointer type is known) and a nil value. Which means // interface is not nil, but calling some interface function // on it will most likely result in nil pointer dereference. var otSpanContext ot.SpanContext if parentBridgeSC != nil { otSpanContext = parentBridgeSC } sctx := newBridgeSpanContext(otelSpan.SpanContext(), otSpanContext) span := newBridgeSpan(otelSpan, sctx, t) return span } // ContextWithBridgeSpan sets up the context with the passed // OpenTelemetry span as the active OpenTracing span. // // This function should be used by the OpenTelemetry tracers that want // to be aware how to operate in the environment using OpenTracing // API. func (t *BridgeTracer) ContextWithBridgeSpan(ctx context.Context, span trace.Span) context.Context { var otSpanContext ot.SpanContext if parentSpan := ot.SpanFromContext(ctx); parentSpan != nil { otSpanContext = parentSpan.Context() } bCtx := newBridgeSpanContext(span.SpanContext(), otSpanContext) bSpan := newBridgeSpan(span, bCtx, t) bSpan.skipDeferHook = true return ot.ContextWithSpan(ctx, bSpan) } // ContextWithSpanHook is an implementation of the OpenTracing tracer // extension interface. It will call the DeferredContextSetupHook // function on the tracer if it implements the // DeferredContextSetupTracerExtension interface. func (t *BridgeTracer) ContextWithSpanHook(ctx context.Context, span ot.Span) context.Context { bSpan, ok := span.(*bridgeSpan) if !ok { t.warningHandler("Encountered a foreign OpenTracing span, will not run a possible deferred context setup hook\n") return ctx } if bSpan.skipDeferHook { return ctx } if tracerWithExtension, ok := bSpan.tracer.setTracer.tracer().(migration.DeferredContextSetupTracerExtension); ok { ctx = tracerWithExtension.DeferredContextSetupHook(ctx, bSpan.otelSpan) } return ctx } func otTagsToOTelAttributesKindAndError(tags map[string]interface{}) ([]attribute.KeyValue, trace.SpanKind, bool) { kind := trace.SpanKindInternal err := false var pairs []attribute.KeyValue for k, v := range tags { switch k { case string(otext.SpanKind): if s, ok := v.(string); ok { switch strings.ToLower(s) { case "client": kind = trace.SpanKindClient case "server": kind = trace.SpanKindServer case "producer": kind = trace.SpanKindProducer case "consumer": kind = trace.SpanKindConsumer } } case string(otext.Error): if b, ok := v.(bool); ok && b { err = true } default: pairs = append(pairs, otTagToOTelLabel(k, v)) } } return pairs, kind, err } // otTagToOTelLabel converts given key-value into attribute.KeyValue. // Note that some conversions are not obvious: // - int -> int64 // - uint -> string // - int32 -> int64 // - uint32 -> int64 // - uint64 -> string // - float32 -> float64 func otTagToOTelLabel(k string, v interface{}) attribute.KeyValue { key := otTagToOTelLabelKey(k) switch val := v.(type) { case bool: return key.Bool(val) case int64: return key.Int64(val) case uint64: return key.String(fmt.Sprintf("%d", val)) case float64: return key.Float64(val) case int32: return key.Int64(int64(val)) case uint32: return key.Int64(int64(val)) case float32: return key.Float64(float64(val)) case int: return key.Int(val) case uint: return key.String(fmt.Sprintf("%d", val)) case string: return key.String(val) default: return key.String(fmt.Sprint(v)) } } func otTagToOTelLabelKey(k string) attribute.Key { return attribute.Key(k) } func otSpanReferencesToParentAndLinks(references []ot.SpanReference) (*bridgeSpanContext, []trace.Link) { var ( parent *bridgeSpanContext links []trace.Link ) for _, reference := range references { bridgeSC, ok := reference.ReferencedContext.(*bridgeSpanContext) if !ok { // We ignore foreign ot span contexts, // sorry. We have no way of getting any // TraceID and SpanID out of it for form a // OTel SpanContext for OTel Link. And // we can't make it a parent - it also needs a // valid OTel SpanContext. continue } if parent != nil { links = append(links, otSpanReferenceToOTelLink(bridgeSC, reference.Type)) } else { if reference.Type == ot.ChildOfRef { parent = bridgeSC } else { links = append(links, otSpanReferenceToOTelLink(bridgeSC, reference.Type)) } } } return parent, links } func otSpanReferenceToOTelLink(bridgeSC *bridgeSpanContext, refType ot.SpanReferenceType) trace.Link { return trace.Link{ SpanContext: bridgeSC.otelSpanContext, Attributes: otSpanReferenceTypeToOTelLinkAttributes(refType), } } func otSpanReferenceTypeToOTelLinkAttributes(refType ot.SpanReferenceType) []attribute.KeyValue { return []attribute.KeyValue{ attribute.String("ot-span-reference-type", otSpanReferenceTypeToString(refType)), } } func otSpanReferenceTypeToString(refType ot.SpanReferenceType) string { switch refType { case ot.ChildOfRef: // "extra", because first child-of reference is used // as a parent, so this function isn't even called for // it. return "extra-child-of" case ot.FollowsFromRef: return "follows-from-ref" default: return fmt.Sprintf("unknown-%d", int(refType)) } } // fakeSpan is just a holder of span context, nothing more. It's for // propagators, so they can get the span context from Go context. type fakeSpan struct { trace.Span sc trace.SpanContext } func (s fakeSpan) SpanContext() trace.SpanContext { return s.sc } // Inject is a part of the implementation of the OpenTracing Tracer // interface. // // Currently only the HTTPHeaders format is supported. func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error { bridgeSC, ok := sm.(*bridgeSpanContext) if !ok { return ot.ErrInvalidSpanContext } if !bridgeSC.otelSpanContext.IsValid() { return ot.ErrInvalidSpanContext } if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders { return ot.ErrUnsupportedFormat } hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier) if !ok { return ot.ErrInvalidCarrier } header := http.Header(hhcarrier) fs := fakeSpan{ Span: noop.Span, sc: bridgeSC.otelSpanContext, } ctx := trace.ContextWithSpan(context.Background(), fs) ctx = baggage.ContextWithBaggage(ctx, bridgeSC.bag) t.getPropagator().Inject(ctx, propagation.HeaderCarrier(header)) return nil } // Extract is a part of the implementation of the OpenTracing Tracer // interface. // // Currently only the HTTPHeaders format is supported. func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) { if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders { return nil, ot.ErrUnsupportedFormat } hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier) if !ok { return nil, ot.ErrInvalidCarrier } header := http.Header(hhcarrier) ctx := t.getPropagator().Extract(context.Background(), propagation.HeaderCarrier(header)) baggage := baggage.FromContext(ctx) bridgeSC := &bridgeSpanContext{ bag: baggage, otelSpanContext: trace.SpanContextFromContext(ctx), } if !bridgeSC.otelSpanContext.IsValid() { return nil, ot.ErrSpanContextNotFound } return bridgeSC, nil } func (t *BridgeTracer) getPropagator() propagation.TextMapPropagator { if t.propagator != nil { return t.propagator } return otel.GetTextMapPropagator() } golang-opentelemetry-otel-1.1.0/bridge/opentracing/doc.go000066400000000000000000000131061414226744000234610ustar00rootroot00000000000000// 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 opentracing implements a bridge that forwards OpenTracing API // calls to the OpenTelemetry SDK. // // To use the bridge, first create an OpenTelemetry tracer of // choice. Then use the NewTracerPair() function to create two tracers // - one implementing OpenTracing API (BridgeTracer) and one that // implements the OpenTelemetry API (WrapperTracer) and mostly // forwards the calls to the OpenTelemetry tracer of choice, but does // some extra steps to make the interaction between both APIs // working. If the OpenTelemetry tracer of choice already knows how to // cooperate with OpenTracing API through the OpenTracing bridge // (explained in detail below), then it is fine to skip the // WrapperTracer by calling the NewBridgeTracer() function to get the // bridge tracer and then passing the chosen OpenTelemetry tracer to // the SetOpenTelemetryTracer() function of the bridge tracer. // // To use an OpenTelemetry span as the parent of an OpenTracing span, // create a context using the ContextWithBridgeSpan() function of // the bridge tracer, and then use the StartSpanFromContext function // of the OpenTracing API. // // Bridge tracer also allows the user to install a warning handler // through the SetWarningHandler() function. The warning handler will // be called when there is some misbehavior of the OpenTelemetry // tracer with regard to the cooperation with the OpenTracing API. // // For an OpenTelemetry tracer to cooperate with OpenTracing API // through the BridgeTracer, the OpenTelemetry tracer needs to // (reasoning is below the list): // // 1. Return the same context it received in the Start() function if // migration.SkipContextSetup() returns true. // // 2. Implement the migration.DeferredContextSetupTracerExtension // interface. The implementation should setup the context it would // normally do in the Start() function if the // migration.SkipContextSetup() function returned false. Calling // ContextWithBridgeSpan() is not necessary. // // 3. Have an access to the BridgeTracer instance. // // 4. If the migration.SkipContextSetup() function returned false, the // tracer should use the ContextWithBridgeSpan() function to install the // created span as an active OpenTracing span. // // There are some differences between OpenTracing and OpenTelemetry // APIs, especially with regard to Go context handling. When a span is // created with an OpenTracing API (through the StartSpan() function) // the Go context is not available. BridgeTracer has access to the // OpenTelemetry tracer of choice, so in the StartSpan() function // BridgeTracer translates the parameters to the OpenTelemetry version // and uses the OpenTelemetry tracer's Start() function to actually // create a span. The OpenTelemetry Start() function takes the Go // context as a parameter, so BridgeTracer at this point passes a // temporary context to Start(). All the changes to the temporary // context will be lost at the end of the StartSpan() function, so the // OpenTelemetry tracer of choice should not do anything with the // context. If the returned context is different, BridgeTracer will // warn about it. The OpenTelemetry tracer of choice can learn about // this situation by using the migration.SkipContextSetup() // function. The tracer will receive an opportunity to set up the // context at a later stage. Usually after StartSpan() is finished, // users of the OpenTracing API are calling (either directly or // through the opentracing.StartSpanFromContext() helper function) the // opentracing.ContextWithSpan() function to insert the created // OpenTracing span into the context. At that time, the OpenTelemetry // tracer of choice has a chance of setting up the context through a // hook invoked inside the opentracing.ContextWithSpan() function. For // that to happen, the tracer should implement the // migration.DeferredContextSetupTracerExtension interface. This so // far explains the need for points 1. and 2. // // When the span is created with the OpenTelemetry API (with the // Start() function) then migration.SkipContextSetup() will return // false. This means that the tracer can do the usual setup of the // context, but it also should set up the active OpenTracing span in // the context. This is because OpenTracing API is not used at all in // the creation of the span, but the OpenTracing API may be used // during the time when the created OpenTelemetry span is current. For // this case to work, we need to also set up active OpenTracing span // in the context. This can be done with the ContextWithBridgeSpan() // function. This means that the OpenTelemetry tracer of choice needs // to have an access to the BridgeTracer instance. This should explain // the need for points 3. and 4. // // Another difference related to the Go context handling is in logging // - OpenTracing API does not take a context parameter in the // LogFields() function, so when the call to the function gets // translated to OpenTelemetry AddEvent() function, an empty context // is passed. package opentracing // import "go.opentelemetry.io/otel/bridge/opentracing" golang-opentelemetry-otel-1.1.0/bridge/opentracing/go.mod000066400000000000000000000053531414226744000235000ustar00rootroot00000000000000module go.opentelemetry.io/otel/bridge/opentracing go 1.15 replace go.opentelemetry.io/otel => ../.. require ( github.com/opentracing/opentracing-go v1.2.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ./ replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/bridge/opentracing/go.sum000066400000000000000000000032311414226744000235160ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/bridge/opentracing/internal/000077500000000000000000000000001414226744000242005ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opentracing/internal/doc.go000066400000000000000000000012501414226744000252720ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/bridge/opentracing/internal" golang-opentelemetry-otel-1.1.0/bridge/opentracing/internal/mock.go000066400000000000000000000166341414226744000254720ustar00rootroot00000000000000// 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 ( "context" "math/rand" "reflect" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/bridge/opentracing/migration" ) //nolint:revive // ignoring missing comments for unexported global variables in an internal package. var ( ComponentKey = attribute.Key("component") ServiceKey = attribute.Key("service") StatusCodeKey = attribute.Key("status.code") StatusMessageKey = attribute.Key("status.message") ErrorKey = attribute.Key("error") NameKey = attribute.Key("name") ) type MockContextKeyValue struct { Key interface{} Value interface{} } type MockTracer struct { FinishedSpans []*MockSpan SpareTraceIDs []trace.TraceID SpareSpanIDs []trace.SpanID SpareContextKeyValues []MockContextKeyValue randLock sync.Mutex rand *rand.Rand } var _ trace.Tracer = &MockTracer{} var _ migration.DeferredContextSetupTracerExtension = &MockTracer{} func NewMockTracer() *MockTracer { return &MockTracer{ FinishedSpans: nil, SpareTraceIDs: nil, SpareSpanIDs: nil, SpareContextKeyValues: nil, rand: rand.New(rand.NewSource(time.Now().Unix())), } } func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { config := trace.NewSpanStartConfig(opts...) startTime := config.Timestamp() if startTime.IsZero() { startTime = time.Now() } spanContext := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: t.getTraceID(ctx, &config), SpanID: t.getSpanID(), TraceFlags: 0, }) span := &MockSpan{ mockTracer: t, officialTracer: t, spanContext: spanContext, Attributes: config.Attributes(), StartTime: startTime, EndTime: time.Time{}, ParentSpanID: t.getParentSpanID(ctx, &config), Events: nil, SpanKind: trace.ValidateSpanKind(config.SpanKind()), } if !migration.SkipContextSetup(ctx) { ctx = trace.ContextWithSpan(ctx, span) ctx = t.addSpareContextValue(ctx) } return ctx, span } func (t *MockTracer) addSpareContextValue(ctx context.Context) context.Context { if len(t.SpareContextKeyValues) > 0 { pair := t.SpareContextKeyValues[0] t.SpareContextKeyValues[0] = MockContextKeyValue{} t.SpareContextKeyValues = t.SpareContextKeyValues[1:] if len(t.SpareContextKeyValues) == 0 { t.SpareContextKeyValues = nil } ctx = context.WithValue(ctx, pair.Key, pair.Value) } return ctx } func (t *MockTracer) getTraceID(ctx context.Context, config *trace.SpanConfig) trace.TraceID { if parent := t.getParentSpanContext(ctx, config); parent.IsValid() { return parent.TraceID() } if len(t.SpareTraceIDs) > 0 { traceID := t.SpareTraceIDs[0] t.SpareTraceIDs = t.SpareTraceIDs[1:] if len(t.SpareTraceIDs) == 0 { t.SpareTraceIDs = nil } return traceID } return t.getRandTraceID() } func (t *MockTracer) getParentSpanID(ctx context.Context, config *trace.SpanConfig) trace.SpanID { if parent := t.getParentSpanContext(ctx, config); parent.IsValid() { return parent.SpanID() } return trace.SpanID{} } func (t *MockTracer) getParentSpanContext(ctx context.Context, config *trace.SpanConfig) trace.SpanContext { if !config.NewRoot() { return trace.SpanContextFromContext(ctx) } return trace.SpanContext{} } func (t *MockTracer) getSpanID() trace.SpanID { if len(t.SpareSpanIDs) > 0 { spanID := t.SpareSpanIDs[0] t.SpareSpanIDs = t.SpareSpanIDs[1:] if len(t.SpareSpanIDs) == 0 { t.SpareSpanIDs = nil } return spanID } return t.getRandSpanID() } func (t *MockTracer) getRandSpanID() trace.SpanID { t.randLock.Lock() defer t.randLock.Unlock() sid := trace.SpanID{} t.rand.Read(sid[:]) return sid } func (t *MockTracer) getRandTraceID() trace.TraceID { t.randLock.Lock() defer t.randLock.Unlock() tid := trace.TraceID{} t.rand.Read(tid[:]) return tid } func (t *MockTracer) DeferredContextSetupHook(ctx context.Context, span trace.Span) context.Context { return t.addSpareContextValue(ctx) } type MockEvent struct { Timestamp time.Time Name string Attributes []attribute.KeyValue } type MockSpan struct { mockTracer *MockTracer officialTracer trace.Tracer spanContext trace.SpanContext SpanKind trace.SpanKind recording bool Attributes []attribute.KeyValue StartTime time.Time EndTime time.Time ParentSpanID trace.SpanID Events []MockEvent } var _ trace.Span = &MockSpan{} var _ migration.OverrideTracerSpanExtension = &MockSpan{} func (s *MockSpan) SpanContext() trace.SpanContext { return s.spanContext } func (s *MockSpan) IsRecording() bool { return s.recording } func (s *MockSpan) SetStatus(code codes.Code, msg string) { s.SetAttributes(StatusCodeKey.Int(int(code)), StatusMessageKey.String(msg)) } func (s *MockSpan) SetName(name string) { s.SetAttributes(NameKey.String(name)) } func (s *MockSpan) SetError(v bool) { s.SetAttributes(ErrorKey.Bool(v)) } func (s *MockSpan) SetAttributes(attributes ...attribute.KeyValue) { s.applyUpdate(attributes) } func (s *MockSpan) applyUpdate(update []attribute.KeyValue) { updateM := make(map[attribute.Key]attribute.Value, len(update)) for _, kv := range update { updateM[kv.Key] = kv.Value } seen := make(map[attribute.Key]struct{}) for i, kv := range s.Attributes { if v, ok := updateM[kv.Key]; ok { s.Attributes[i].Value = v seen[kv.Key] = struct{}{} } } for k, v := range updateM { if _, ok := seen[k]; ok { continue } s.Attributes = append(s.Attributes, attribute.KeyValue{Key: k, Value: v}) } } func (s *MockSpan) End(options ...trace.SpanEndOption) { if !s.EndTime.IsZero() { return // already finished } config := trace.NewSpanEndConfig(options...) endTime := config.Timestamp() if endTime.IsZero() { endTime = time.Now() } s.EndTime = endTime s.mockTracer.FinishedSpans = append(s.mockTracer.FinishedSpans, s) } func (s *MockSpan) RecordError(err error, opts ...trace.EventOption) { if err == nil { return // no-op on nil error } if !s.EndTime.IsZero() { return // already finished } s.SetStatus(codes.Error, "") opts = append(opts, trace.WithAttributes( semconv.ExceptionTypeKey.String(reflect.TypeOf(err).String()), semconv.ExceptionMessageKey.String(err.Error()), )) s.AddEvent(semconv.ExceptionEventName, opts...) } func (s *MockSpan) Tracer() trace.Tracer { return s.officialTracer } func (s *MockSpan) AddEvent(name string, o ...trace.EventOption) { c := trace.NewEventConfig(o...) s.Events = append(s.Events, MockEvent{ Timestamp: c.Timestamp(), Name: name, Attributes: c.Attributes(), }) } func (s *MockSpan) OverrideTracer(tracer trace.Tracer) { s.officialTracer = tracer } func (s *MockSpan) TracerProvider() trace.TracerProvider { return trace.NewNoopTracerProvider() } golang-opentelemetry-otel-1.1.0/bridge/opentracing/migration/000077500000000000000000000000001414226744000243555ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/bridge/opentracing/migration/api.go000066400000000000000000000065271414226744000254670ustar00rootroot00000000000000// 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 migration provides interfaces and functions that are useful for // providing a cooperation of the OpenTelemetry tracers with the // OpenTracing API. package migration // import "go.opentelemetry.io/otel/bridge/opentracing/migration" import ( "context" "go.opentelemetry.io/otel/trace" ) // DeferredContextSetupTracerExtension is an interface an // OpenTelemetry tracer may implement in order to cooperate with the // calls to the OpenTracing API. // // Tracers implementing this interface should also use the // SkipContextSetup() function during creation of the span in the // Start() function to skip the configuration of the context. type DeferredContextSetupTracerExtension interface { // DeferredContextSetupHook is called by the bridge // OpenTracing tracer when opentracing.ContextWithSpan is // called. This allows the OpenTelemetry tracer to set up the // context in a way it would normally do during the Start() // function. Since OpenTracing API does not support // configuration of the context during span creation, it needs // to be deferred until the call to the // opentracing.ContextWithSpan happens. When bridge // OpenTracing tracer calls OpenTelemetry tracer's Start() // function, it passes a context that shouldn't be modified. DeferredContextSetupHook(ctx context.Context, span trace.Span) context.Context } // OverrideTracerSpanExtension is an interface an OpenTelemetry span // may implement in order to cooperate with the calls to the // OpenTracing API. // // TODO(krnowak): I'm actually not so sold on the idea… The reason for // introducing this interface was to have a span "created" by the // WrapperTracer return WrapperTracer from the Tracer() function, not // the real OpenTelemetry tracer that actually created the span. I'm // thinking that I could create a wrapperSpan type that wraps an // OpenTelemetry Span object and have WrapperTracer to alter the // current OpenTelemetry span in the context so it points to the // wrapped object, so the code in the tracer like // `trace.SpanFromContent().(*realSpan)` would still work. Another // argument for getting rid of this interface is that is only called // by the WrapperTracer - WrapperTracer likely shouldn't require any // changes in the underlying OpenTelemetry tracer to have things // somewhat working. // // See the "tracer mess" test in mix_test.go. type OverrideTracerSpanExtension interface { // OverrideTracer makes the span to return the passed tracer // from its Tracer() function. // // You don't need to implement this function if your // OpenTelemetry tracer cooperates well with the OpenTracing // API calls. In such case, there is no need to use the // WrapperTracer and thus no need to override the result of // the Tracer() function. OverrideTracer(tracer trace.Tracer) } golang-opentelemetry-otel-1.1.0/bridge/opentracing/migration/defer.go000066400000000000000000000024371414226744000257770ustar00rootroot00000000000000// 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 migration // import "go.opentelemetry.io/otel/bridge/opentracing/migration" import ( "context" ) type doDeferredContextSetupType struct{} // WithDeferredSetup returns a context that can tell the OpenTelemetry // tracer to skip the context setup in the Start() function. func WithDeferredSetup(ctx context.Context) context.Context { return context.WithValue(ctx, doDeferredContextSetupType{}, doDeferredContextSetupType{}) } // SkipContextSetup can tell the OpenTelemetry tracer to skip the // context setup during the span creation in the Start() function. func SkipContextSetup(ctx context.Context) bool { _, ok := ctx.Value(doDeferredContextSetupType{}).(doDeferredContextSetupType) return ok } golang-opentelemetry-otel-1.1.0/bridge/opentracing/mix_test.go000066400000000000000000000510221414226744000245470ustar00rootroot00000000000000// 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 opentracing import ( "context" "fmt" "testing" ot "github.com/opentracing/opentracing-go" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/bridge/opentracing/internal" ) type mixedAPIsTestCase struct { desc string setup func(*testing.T, *internal.MockTracer) run func(*testing.T, context.Context) check func(*testing.T, *internal.MockTracer) } func getMixedAPIsTestCases() []mixedAPIsTestCase { st := newSimpleTest() cast := newCurrentActiveSpanTest() coin := newContextIntactTest() bip := newBaggageItemsPreservationTest() bio := newBaggageInteroperationTest() return []mixedAPIsTestCase{ { desc: "simple otel -> ot -> otel", setup: st.setup, run: st.runOtelOTOtel, check: st.check, }, { desc: "simple ot -> otel -> ot", setup: st.setup, run: st.runOTOtelOT, check: st.check, }, { desc: "current/active span otel -> ot -> otel", setup: cast.setup, run: cast.runOtelOTOtel, check: cast.check, }, { desc: "current/active span ot -> otel -> ot", setup: cast.setup, run: cast.runOTOtelOT, check: cast.check, }, { desc: "context intact otel -> ot -> otel", setup: coin.setup, run: coin.runOtelOTOtel, check: coin.check, }, { desc: "context intact ot -> otel -> ot", setup: coin.setup, run: coin.runOTOtelOT, check: coin.check, }, { desc: "baggage items preservation across layers otel -> ot -> otel", setup: bip.setup, run: bip.runOtelOTOtel, check: bip.check, }, { desc: "baggage items preservation across layers ot -> otel -> ot", setup: bip.setup, run: bip.runOTOtelOT, check: bip.check, }, { desc: "baggage items interoperation across layers ot -> otel -> ot", setup: bio.setup, run: bio.runOTOtelOT, check: bio.check, }, { desc: "baggage items interoperation across layers otel -> ot -> otel", setup: bio.setup, run: bio.runOtelOTOtel, check: bio.check, }, } } func TestMixedAPIs(t *testing.T) { for idx, tc := range getMixedAPIsTestCases() { t.Logf("Running test case %d: %s", idx, tc.desc) mockOtelTracer := internal.NewMockTracer() ctx, otTracer, otelProvider := NewTracerPairWithContext(context.Background(), mockOtelTracer) otTracer.SetWarningHandler(func(msg string) { t.Log(msg) }) otel.SetTracerProvider(otelProvider) ot.SetGlobalTracer(otTracer) tc.setup(t, mockOtelTracer) tc.run(t, ctx) tc.check(t, mockOtelTracer) } } // simple test type simpleTest struct { traceID trace.TraceID spanIDs []trace.SpanID } func newSimpleTest() *simpleTest { return &simpleTest{ traceID: simpleTraceID(), spanIDs: simpleSpanIDs(3), } } func (st *simpleTest) setup(t *testing.T, tracer *internal.MockTracer) { tracer.SpareTraceIDs = append(tracer.SpareTraceIDs, st.traceID) tracer.SpareSpanIDs = append(tracer.SpareSpanIDs, st.spanIDs...) } func (st *simpleTest) check(t *testing.T, tracer *internal.MockTracer) { checkTraceAndSpans(t, tracer, st.traceID, st.spanIDs) } func (st *simpleTest) runOtelOTOtel(t *testing.T, ctx context.Context) { runOtelOTOtel(t, ctx, "simple", st.noop) } func (st *simpleTest) runOTOtelOT(t *testing.T, ctx context.Context) { runOTOtelOT(t, ctx, "simple", st.noop) } func (st *simpleTest) noop(t *testing.T, ctx context.Context) context.Context { return ctx } // current/active span test type currentActiveSpanTest struct { traceID trace.TraceID spanIDs []trace.SpanID recordedCurrentOtelSpanIDs []trace.SpanID recordedActiveOTSpanIDs []trace.SpanID } func newCurrentActiveSpanTest() *currentActiveSpanTest { return ¤tActiveSpanTest{ traceID: simpleTraceID(), spanIDs: simpleSpanIDs(3), } } func (cast *currentActiveSpanTest) setup(t *testing.T, tracer *internal.MockTracer) { tracer.SpareTraceIDs = append(tracer.SpareTraceIDs, cast.traceID) tracer.SpareSpanIDs = append(tracer.SpareSpanIDs, cast.spanIDs...) cast.recordedCurrentOtelSpanIDs = nil cast.recordedActiveOTSpanIDs = nil } func (cast *currentActiveSpanTest) check(t *testing.T, tracer *internal.MockTracer) { checkTraceAndSpans(t, tracer, cast.traceID, cast.spanIDs) if len(cast.recordedCurrentOtelSpanIDs) != len(cast.spanIDs) { t.Errorf("Expected to have %d recorded Otel current spans, got %d", len(cast.spanIDs), len(cast.recordedCurrentOtelSpanIDs)) } if len(cast.recordedActiveOTSpanIDs) != len(cast.spanIDs) { t.Errorf("Expected to have %d recorded OT active spans, got %d", len(cast.spanIDs), len(cast.recordedActiveOTSpanIDs)) } minLen := min(len(cast.recordedCurrentOtelSpanIDs), len(cast.spanIDs)) minLen = min(minLen, len(cast.recordedActiveOTSpanIDs)) for i := 0; i < minLen; i++ { if cast.recordedCurrentOtelSpanIDs[i] != cast.spanIDs[i] { t.Errorf("Expected span idx %d (%d) to be recorded as current span in Otel, got %d", i, cast.spanIDs[i], cast.recordedCurrentOtelSpanIDs[i]) } if cast.recordedActiveOTSpanIDs[i] != cast.spanIDs[i] { t.Errorf("Expected span idx %d (%d) to be recorded as active span in OT, got %d", i, cast.spanIDs[i], cast.recordedActiveOTSpanIDs[i]) } } } func (cast *currentActiveSpanTest) runOtelOTOtel(t *testing.T, ctx context.Context) { runOtelOTOtel(t, ctx, "cast", cast.recordSpans) } func (cast *currentActiveSpanTest) runOTOtelOT(t *testing.T, ctx context.Context) { runOTOtelOT(t, ctx, "cast", cast.recordSpans) } func (cast *currentActiveSpanTest) recordSpans(t *testing.T, ctx context.Context) context.Context { spanID := trace.SpanContextFromContext(ctx).SpanID() cast.recordedCurrentOtelSpanIDs = append(cast.recordedCurrentOtelSpanIDs, spanID) spanID = trace.SpanID{} if bridgeSpan, ok := ot.SpanFromContext(ctx).(*bridgeSpan); ok { spanID = bridgeSpan.otelSpan.SpanContext().SpanID() } cast.recordedActiveOTSpanIDs = append(cast.recordedActiveOTSpanIDs, spanID) return ctx } // context intact test type contextIntactTest struct { contextKeyValues []internal.MockContextKeyValue recordedContextValues []interface{} recordIdx int } type coin1Key struct{} type coin1Value struct{} type coin2Key struct{} type coin2Value struct{} type coin3Key struct{} type coin3Value struct{} func newContextIntactTest() *contextIntactTest { return &contextIntactTest{ contextKeyValues: []internal.MockContextKeyValue{ { Key: coin1Key{}, Value: coin1Value{}, }, { Key: coin2Key{}, Value: coin2Value{}, }, { Key: coin3Key{}, Value: coin3Value{}, }, }, } } func (coin *contextIntactTest) setup(t *testing.T, tracer *internal.MockTracer) { tracer.SpareContextKeyValues = append(tracer.SpareContextKeyValues, coin.contextKeyValues...) coin.recordedContextValues = nil coin.recordIdx = 0 } func (coin *contextIntactTest) check(t *testing.T, tracer *internal.MockTracer) { if len(coin.recordedContextValues) != len(coin.contextKeyValues) { t.Errorf("Expected to have %d recorded context values, got %d", len(coin.contextKeyValues), len(coin.recordedContextValues)) } minLen := min(len(coin.recordedContextValues), len(coin.contextKeyValues)) for i := 0; i < minLen; i++ { key := coin.contextKeyValues[i].Key value := coin.contextKeyValues[i].Value gotValue := coin.recordedContextValues[i] if value != gotValue { t.Errorf("Expected value %#v for key %#v, got %#v", value, key, gotValue) } } } func (coin *contextIntactTest) runOtelOTOtel(t *testing.T, ctx context.Context) { runOtelOTOtel(t, ctx, "coin", coin.recordValue) } func (coin *contextIntactTest) runOTOtelOT(t *testing.T, ctx context.Context) { runOTOtelOT(t, ctx, "coin", coin.recordValue) } func (coin *contextIntactTest) recordValue(t *testing.T, ctx context.Context) context.Context { if coin.recordIdx >= len(coin.contextKeyValues) { t.Errorf("Too many steps?") return ctx } key := coin.contextKeyValues[coin.recordIdx].Key coin.recordIdx++ coin.recordedContextValues = append(coin.recordedContextValues, ctx.Value(key)) return ctx } // baggage items preservation test type bipBaggage struct { key string value string } type baggageItemsPreservationTest struct { baggageItems []bipBaggage step int recordedBaggage []map[string]string } func newBaggageItemsPreservationTest() *baggageItemsPreservationTest { return &baggageItemsPreservationTest{ baggageItems: []bipBaggage{ { key: "First", value: "one", }, { key: "Second", value: "two", }, { key: "Third", value: "three", }, }, } } func (bip *baggageItemsPreservationTest) setup(t *testing.T, tracer *internal.MockTracer) { bip.step = 0 bip.recordedBaggage = nil } func (bip *baggageItemsPreservationTest) check(t *testing.T, tracer *internal.MockTracer) { if len(bip.recordedBaggage) != len(bip.baggageItems) { t.Errorf("Expected %d recordings, got %d", len(bip.baggageItems), len(bip.recordedBaggage)) } minLen := min(len(bip.recordedBaggage), len(bip.baggageItems)) for i := 0; i < minLen; i++ { recordedItems := bip.recordedBaggage[i] if len(recordedItems) != i+1 { t.Errorf("Expected %d recorded baggage items in recording %d, got %d", i+1, i+1, len(bip.recordedBaggage[i])) } minItemLen := min(len(bip.baggageItems), i+1) for j := 0; j < minItemLen; j++ { expectedItem := bip.baggageItems[j] if gotValue, ok := recordedItems[expectedItem.key]; !ok { t.Errorf("Missing baggage item %q in recording %d", expectedItem.key, i+1) } else if gotValue != expectedItem.value { t.Errorf("Expected recorded baggage item %q in recording %d + 1to be %q, got %q", expectedItem.key, i, expectedItem.value, gotValue) } else { delete(recordedItems, expectedItem.key) } } for key, value := range recordedItems { t.Errorf("Unexpected baggage item in recording %d: %q -> %q", i+1, key, value) } } } func (bip *baggageItemsPreservationTest) runOtelOTOtel(t *testing.T, ctx context.Context) { runOtelOTOtel(t, ctx, "bip", bip.addAndRecordBaggage) } func (bip *baggageItemsPreservationTest) runOTOtelOT(t *testing.T, ctx context.Context) { runOTOtelOT(t, ctx, "bip", bip.addAndRecordBaggage) } func (bip *baggageItemsPreservationTest) addAndRecordBaggage(t *testing.T, ctx context.Context) context.Context { if bip.step >= len(bip.baggageItems) { t.Errorf("Too many steps?") return ctx } span := ot.SpanFromContext(ctx) if span == nil { t.Errorf("No active OpenTracing span") return ctx } idx := bip.step bip.step++ span.SetBaggageItem(bip.baggageItems[idx].key, bip.baggageItems[idx].value) sctx := span.Context() recording := make(map[string]string) sctx.ForeachBaggageItem(func(key, value string) bool { recording[key] = value return true }) bip.recordedBaggage = append(bip.recordedBaggage, recording) return ctx } // baggage interoperation test type baggageInteroperationTest struct { baggageItems []bipBaggage step int recordedOTBaggage []map[string]string recordedOtelBaggage []map[string]string } func newBaggageInteroperationTest() *baggageInteroperationTest { return &baggageInteroperationTest{ baggageItems: []bipBaggage{ { key: "First", value: "one", }, { key: "Second", value: "two", }, { key: "Third", value: "three", }, }, } } func (bio *baggageInteroperationTest) setup(t *testing.T, tracer *internal.MockTracer) { bio.step = 0 bio.recordedOTBaggage = nil bio.recordedOtelBaggage = nil } func (bio *baggageInteroperationTest) check(t *testing.T, tracer *internal.MockTracer) { checkBIORecording(t, "OT", bio.baggageItems, bio.recordedOTBaggage) checkBIORecording(t, "Otel", bio.baggageItems, bio.recordedOtelBaggage) } func checkBIORecording(t *testing.T, apiDesc string, initialItems []bipBaggage, recordings []map[string]string) { // expect recordings count to equal the number of initial // items // each recording should have a duplicated item from initial // items, one with OT suffix, another one with Otel suffix // expect each subsequent recording to have two more items, up // to double of the count of the initial items if len(initialItems) != len(recordings) { t.Errorf("Expected %d recordings from %s, got %d", len(initialItems), apiDesc, len(recordings)) } minRecLen := min(len(initialItems), len(recordings)) for i := 0; i < minRecLen; i++ { recordedItems := recordings[i] expectedItemsInStep := (i + 1) * 2 if expectedItemsInStep != len(recordedItems) { t.Errorf("Expected %d recorded items in recording %d from %s, got %d", expectedItemsInStep, i, apiDesc, len(recordedItems)) } recordedItemsCopy := make(map[string]string, len(recordedItems)) for k, v := range recordedItems { recordedItemsCopy[k] = v } for j := 0; j < i+1; j++ { otKey, otelKey := generateBaggageKeys(initialItems[j].key) value := initialItems[j].value for _, k := range []string{otKey, otelKey} { if v, ok := recordedItemsCopy[k]; ok { if value != v { t.Errorf("Expected value %s under key %s in recording %d from %s, got %s", value, k, i, apiDesc, v) } delete(recordedItemsCopy, k) } else { t.Errorf("Missing key %s in recording %d from %s", k, i, apiDesc) } } } for k, v := range recordedItemsCopy { t.Errorf("Unexpected key-value pair %s = %s in recording %d from %s", k, v, i, apiDesc) } } } func (bio *baggageInteroperationTest) runOtelOTOtel(t *testing.T, ctx context.Context) { runOtelOTOtel(t, ctx, "bio", bio.addAndRecordBaggage) } func (bio *baggageInteroperationTest) runOTOtelOT(t *testing.T, ctx context.Context) { runOTOtelOT(t, ctx, "bio", bio.addAndRecordBaggage) } func (bio *baggageInteroperationTest) addAndRecordBaggage(t *testing.T, ctx context.Context) context.Context { if bio.step >= len(bio.baggageItems) { t.Errorf("Too many steps?") return ctx } otSpan := ot.SpanFromContext(ctx) if otSpan == nil { t.Errorf("No active OpenTracing span") return ctx } idx := bio.step bio.step++ key := bio.baggageItems[idx].key otKey, otelKey := generateBaggageKeys(key) value := bio.baggageItems[idx].value otSpan.SetBaggageItem(otKey, value) m, err := baggage.NewMember(otelKey, value) if err != nil { t.Error(err) return ctx } b, err := baggage.FromContext(ctx).SetMember(m) if err != nil { t.Error(err) return ctx } ctx = baggage.ContextWithBaggage(ctx, b) otRecording := make(map[string]string) otSpan.Context().ForeachBaggageItem(func(key, value string) bool { otRecording[key] = value return true }) otelRecording := make(map[string]string) for _, m := range baggage.FromContext(ctx).Members() { otelRecording[m.Key()] = m.Value() } bio.recordedOTBaggage = append(bio.recordedOTBaggage, otRecording) bio.recordedOtelBaggage = append(bio.recordedOtelBaggage, otelRecording) return ctx } func generateBaggageKeys(key string) (otKey, otelKey string) { otKey, otelKey = key+"-Ot", key+"-Otel" return } // helpers func checkTraceAndSpans(t *testing.T, tracer *internal.MockTracer, expectedTraceID trace.TraceID, expectedSpanIDs []trace.SpanID) { expectedSpanCount := len(expectedSpanIDs) // reverse spanIDs, since first span ID belongs to root, that // finishes last spanIDs := make([]trace.SpanID, len(expectedSpanIDs)) copy(spanIDs, expectedSpanIDs) reverse(len(spanIDs), func(i, j int) { spanIDs[i], spanIDs[j] = spanIDs[j], spanIDs[i] }) // the last finished span has no parent parentSpanIDs := append(spanIDs[1:], trace.SpanID{}) sks := map[trace.SpanID]trace.SpanKind{ {125}: trace.SpanKindProducer, {124}: trace.SpanKindInternal, {123}: trace.SpanKindClient, } if len(tracer.FinishedSpans) != expectedSpanCount { t.Errorf("Expected %d finished spans, got %d", expectedSpanCount, len(tracer.FinishedSpans)) } for idx, span := range tracer.FinishedSpans { sctx := span.SpanContext() if sctx.TraceID() != expectedTraceID { t.Errorf("Expected trace ID %v in span %d (%d), got %v", expectedTraceID, idx, sctx.SpanID(), sctx.TraceID()) } expectedSpanID := spanIDs[idx] expectedParentSpanID := parentSpanIDs[idx] if sctx.SpanID() != expectedSpanID { t.Errorf("Expected finished span %d to have span ID %d, but got %d", idx, expectedSpanID, sctx.SpanID()) } if span.ParentSpanID != expectedParentSpanID { t.Errorf("Expected finished span %d (span ID: %d) to have parent span ID %d, but got %d", idx, sctx.SpanID(), expectedParentSpanID, span.ParentSpanID) } if span.SpanKind != sks[span.SpanContext().SpanID()] { t.Errorf("Expected finished span %d (span ID: %d) to have span.kind to be '%v' but was '%v'", idx, sctx.SpanID(), sks[span.SpanContext().SpanID()], span.SpanKind) } } } func reverse(length int, swap func(i, j int)) { for left, right := 0, length-1; left < right; left, right = left+1, right-1 { swap(left, right) } } func simpleTraceID() trace.TraceID { return [16]byte{123, 42} } func simpleSpanIDs(count int) []trace.SpanID { base := []trace.SpanID{ {123}, {124}, {125}, {126}, {127}, {128}, } return base[:count] } func min(a, b int) int { if a > b { return b } return a } func runOtelOTOtel(t *testing.T, ctx context.Context, name string, callback func(*testing.T, context.Context) context.Context) { tr := otel.Tracer("") ctx, span := tr.Start(ctx, fmt.Sprintf("%s_Otel_OTOtel", name), trace.WithSpanKind(trace.SpanKindClient)) defer span.End() ctx = callback(t, ctx) func(ctx2 context.Context) { span, ctx2 := ot.StartSpanFromContext(ctx2, fmt.Sprintf("%sOtel_OT_Otel", name)) defer span.Finish() ctx2 = callback(t, ctx2) func(ctx3 context.Context) { ctx3, span := tr.Start(ctx3, fmt.Sprintf("%sOtelOT_Otel_", name), trace.WithSpanKind(trace.SpanKindProducer)) defer span.End() _ = callback(t, ctx3) }(ctx2) }(ctx) } func runOTOtelOT(t *testing.T, ctx context.Context, name string, callback func(*testing.T, context.Context) context.Context) { tr := otel.Tracer("") span, ctx := ot.StartSpanFromContext(ctx, fmt.Sprintf("%s_OT_OtelOT", name), ot.Tag{Key: "span.kind", Value: "client"}) defer span.Finish() ctx = callback(t, ctx) func(ctx2 context.Context) { ctx2, span := tr.Start(ctx2, fmt.Sprintf("%sOT_Otel_OT", name)) defer span.End() ctx2 = callback(t, ctx2) func(ctx3 context.Context) { span, ctx3 := ot.StartSpanFromContext(ctx3, fmt.Sprintf("%sOTOtel_OT_", name), ot.Tag{Key: "span.kind", Value: "producer"}) defer span.Finish() _ = callback(t, ctx3) }(ctx2) }(ctx) } func TestOtTagToOTelLabel_CheckTypeConversions(t *testing.T) { tableTest := []struct { key string value interface{} expectedValueType attribute.Type }{ { key: "bool to bool", value: true, expectedValueType: attribute.BOOL, }, { key: "int to int64", value: 123, expectedValueType: attribute.INT64, }, { key: "uint to string", value: uint(1234), expectedValueType: attribute.STRING, }, { key: "int32 to int64", value: int32(12345), expectedValueType: attribute.INT64, }, { key: "uint32 to int64", value: uint32(123456), expectedValueType: attribute.INT64, }, { key: "int64 to int64", value: int64(1234567), expectedValueType: attribute.INT64, }, { key: "uint64 to string", value: uint64(12345678), expectedValueType: attribute.STRING, }, { key: "float32 to float64", value: float32(3.14), expectedValueType: attribute.FLOAT64, }, { key: "float64 to float64", value: float64(3.14), expectedValueType: attribute.FLOAT64, }, { key: "string to string", value: "string_value", expectedValueType: attribute.STRING, }, { key: "unexpected type to string", value: struct{}{}, expectedValueType: attribute.STRING, }, } for _, test := range tableTest { got := otTagToOTelLabel(test.key, test.value) if test.expectedValueType != got.Value.Type() { t.Errorf("Expected type %s, but got %s after conversion '%v' value", test.expectedValueType, got.Value.Type(), test.value) } } } golang-opentelemetry-otel-1.1.0/bridge/opentracing/util.go000066400000000000000000000033161414226744000236730ustar00rootroot00000000000000// 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 opentracing // import "go.opentelemetry.io/otel/bridge/opentracing" import ( "context" "go.opentelemetry.io/otel/trace" ) // NewTracerPair is a utility function that creates a BridgeTracer and a // WrapperTracerProvider. WrapperTracerProvider creates a single instance of // WrapperTracer. The BridgeTracer forwards the calls to the WrapperTracer // that wraps the passed tracer. BridgeTracer and WrapperTracerProvider are // returned to the caller and the caller is expected to register BridgeTracer // with opentracing and WrapperTracerProvider with opentelemetry. func NewTracerPair(tracer trace.Tracer) (*BridgeTracer, *WrapperTracerProvider) { bridgeTracer := NewBridgeTracer() wrapperProvider := NewWrappedTracerProvider(bridgeTracer, tracer) bridgeTracer.SetOpenTelemetryTracer(wrapperProvider.Tracer("")) return bridgeTracer, wrapperProvider } func NewTracerPairWithContext(ctx context.Context, tracer trace.Tracer) (context.Context, *BridgeTracer, *WrapperTracerProvider) { bridgeTracer, wrapperProvider := NewTracerPair(tracer) ctx = bridgeTracer.NewHookedContext(ctx) return ctx, bridgeTracer, wrapperProvider } golang-opentelemetry-otel-1.1.0/bridge/opentracing/wrapper.go000066400000000000000000000066331414226744000244030ustar00rootroot00000000000000// 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 opentracing // import "go.opentelemetry.io/otel/bridge/opentracing" import ( "context" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/bridge/opentracing/migration" ) type WrapperTracerProvider struct { wTracer *WrapperTracer } var _ trace.TracerProvider = (*WrapperTracerProvider)(nil) // Tracer returns the WrapperTracer associated with the WrapperTracerProvider. func (p *WrapperTracerProvider) Tracer(_ string, _ ...trace.TracerOption) trace.Tracer { return p.wTracer } // NewWrappedTracerProvider creates a new trace provider that creates a single // instance of WrapperTracer that wraps OpenTelemetry tracer. func NewWrappedTracerProvider(bridge *BridgeTracer, tracer trace.Tracer) *WrapperTracerProvider { return &WrapperTracerProvider{ wTracer: NewWrapperTracer(bridge, tracer), } } // WrapperTracer is a wrapper around an OpenTelemetry tracer. It // mostly forwards the calls to the wrapped tracer, but also does some // extra steps like setting up a context with the active OpenTracing // span. // // It does not need to be used when the OpenTelemetry tracer is also // aware how to operate in environment where OpenTracing API is also // used. type WrapperTracer struct { bridge *BridgeTracer tracer trace.Tracer } var _ trace.Tracer = &WrapperTracer{} var _ migration.DeferredContextSetupTracerExtension = &WrapperTracer{} // NewWrapperTracer wraps the passed tracer and also talks to the // passed bridge tracer when setting up the context with the new // active OpenTracing span. func NewWrapperTracer(bridge *BridgeTracer, tracer trace.Tracer) *WrapperTracer { return &WrapperTracer{ bridge: bridge, tracer: tracer, } } func (t *WrapperTracer) otelTracer() trace.Tracer { return t.tracer } // Start forwards the call to the wrapped tracer. It also tries to // override the tracer of the returned span if the span implements the // OverrideTracerSpanExtension interface. func (t *WrapperTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { ctx, span := t.otelTracer().Start(ctx, name, opts...) if spanWithExtension, ok := span.(migration.OverrideTracerSpanExtension); ok { spanWithExtension.OverrideTracer(t) } if !migration.SkipContextSetup(ctx) { ctx = t.bridge.ContextWithBridgeSpan(ctx, span) } return ctx, span } // DeferredContextSetupHook is a part of the implementation of the // DeferredContextSetupTracerExtension interface. It will try to // forward the call to the wrapped tracer if it implements the // interface. func (t *WrapperTracer) DeferredContextSetupHook(ctx context.Context, span trace.Span) context.Context { if tracerWithExtension, ok := t.otelTracer().(migration.DeferredContextSetupTracerExtension); ok { ctx = tracerWithExtension.DeferredContextSetupHook(ctx, span) } ctx = trace.ContextWithSpan(ctx, span) return ctx } golang-opentelemetry-otel-1.1.0/codes/000077500000000000000000000000001414226744000177145ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/codes/codes.go000066400000000000000000000052501414226744000213420ustar00rootroot00000000000000// 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 codes // import "go.opentelemetry.io/otel/codes" import ( "encoding/json" "fmt" "strconv" ) const ( // Unset is the default status code. Unset Code = 0 // Error indicates the operation contains an error. Error Code = 1 // Ok indicates operation has been validated by an Application developers // or Operator to have completed successfully, or contain no error. Ok Code = 2 maxCode = 3 ) // Code is an 32-bit representation of a status state. type Code uint32 var codeToStr = map[Code]string{ Unset: "Unset", Error: "Error", Ok: "Ok", } var strToCode = map[string]Code{ `"Unset"`: Unset, `"Error"`: Error, `"Ok"`: Ok, } // String returns the Code as a string. func (c Code) String() string { return codeToStr[c] } // UnmarshalJSON unmarshals b into the Code. // // This is based on the functionality in the gRPC codes package: // https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244 func (c *Code) UnmarshalJSON(b []byte) error { // From json.Unmarshaler: By convention, to approximate the behavior of // Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as // a no-op. if string(b) == "null" { return nil } if c == nil { return fmt.Errorf("nil receiver passed to UnmarshalJSON") } var x interface{} if err := json.Unmarshal(b, &x); err != nil { return err } switch x.(type) { case string: if jc, ok := strToCode[string(b)]; ok { *c = jc return nil } return fmt.Errorf("invalid code: %q", string(b)) case float64: if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil { if ci >= maxCode { return fmt.Errorf("invalid code: %q", ci) } *c = Code(ci) return nil } return fmt.Errorf("invalid code: %q", string(b)) default: return fmt.Errorf("invalid code: %q", string(b)) } } // MarshalJSON returns c as the JSON encoding of c. func (c *Code) MarshalJSON() ([]byte, error) { if c == nil { return []byte("null"), nil } str, ok := codeToStr[*c] if !ok { return nil, fmt.Errorf("invalid code: %d", *c) } return []byte(fmt.Sprintf("%q", str)), nil } golang-opentelemetry-otel-1.1.0/codes/codes_test.go000066400000000000000000000073651414226744000224120ustar00rootroot00000000000000// 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 codes import ( "bytes" "encoding/json" "fmt" "testing" ) func TestCodeString(t *testing.T) { tests := []struct { code Code want string }{ {Unset, "Unset"}, {Error, "Error"}, {Ok, "Ok"}, } for _, test := range tests { if got := test.code.String(); got != test.want { t.Errorf("String of code %d %q, want %q", test.code, got, test.want) } } } func TestCodeUnmarshalJSONNull(t *testing.T) { c := new(Code) orig := c if err := c.UnmarshalJSON([]byte("null")); err != nil { t.Fatalf("Code.UnmarshalJSON(\"null\") errored: %v", err) } if orig != c { t.Error("Code.UnmarshalJSON(\"null\") should not decode a value") } } func TestCodeUnmarshalJSONNil(t *testing.T) { c := (*Code)(nil) if err := c.UnmarshalJSON([]byte{}); err == nil { t.Fatalf("Code(nil).UnmarshalJSON() did not error") } } func TestCodeUnmarshalJSON(t *testing.T) { tests := []struct { input string want Code }{ {"0", Unset}, {`"Unset"`, Unset}, {"1", Error}, {`"Error"`, Error}, {"2", Ok}, {`"Ok"`, Ok}, } for _, test := range tests { c := new(Code) *c = Code(maxCode) if err := json.Unmarshal([]byte(test.input), c); err != nil { t.Fatalf("json.Unmarshal(%q, Code) errored: %v", test.input, err) } if *c != test.want { t.Errorf("failed to unmarshal %q as %v", test.input, test.want) } } } func TestCodeUnmarshalJSONErrorInvalidData(t *testing.T) { tests := []string{ fmt.Sprintf("%d", maxCode), "Not a code", "Unset", "true", `"Not existing"`, "", } c := new(Code) for _, test := range tests { if err := json.Unmarshal([]byte(test), c); err == nil { t.Fatalf("json.Unmarshal(%q, Code) did not error", test) } } } func TestCodeMarshalJSONNil(t *testing.T) { c := (*Code)(nil) b, err := c.MarshalJSON() if err != nil { t.Fatalf("Code(nil).MarshalJSON() errored: %v", err) } if !bytes.Equal(b, []byte("null")) { t.Errorf("Code(nil).MarshalJSON() returned %s, want \"null\"", string(b)) } } func TestCodeMarshalJSON(t *testing.T) { tests := []struct { code Code want string }{ {Unset, `"Unset"`}, {Error, `"Error"`}, {Ok, `"Ok"`}, } for _, test := range tests { b, err := test.code.MarshalJSON() if err != nil { t.Fatalf("Code(%s).MarshalJSON() errored: %v", test.code, err) } if !bytes.Equal(b, []byte(test.want)) { t.Errorf("Code(%s).MarshalJSON() returned %s, want %s", test.code, string(b), test.want) } } } func TestCodeMarshalJSONErrorInvalid(t *testing.T) { c := new(Code) *c = Code(maxCode) if b, err := c.MarshalJSON(); err == nil { t.Fatalf("Code(maxCode).MarshalJSON() did not error") } else if b != nil { t.Fatal("Code(maxCode).MarshalJSON() returned non-nil value") } } func TestRoundTripCodes(t *testing.T) { tests := []struct { input Code }{ {Unset}, {Error}, {Ok}, } for _, test := range tests { c := test.input out := new(Code) b, err := c.MarshalJSON() if err != nil { t.Fatalf("Code(%s).MarshalJSON() errored: %v", test.input, err) } if err := out.UnmarshalJSON(b); err != nil { t.Fatalf("Code.UnmarshalJSON(%q) errored: %v", c, err) } if *out != test.input { t.Errorf("failed to round trip %q, output was %v", test.input, out) } } } golang-opentelemetry-otel-1.1.0/codes/doc.go000066400000000000000000000016071414226744000210140ustar00rootroot00000000000000// 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 codes defines the canonical error codes used by OpenTelemetry. It conforms to [the OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#statuscanonicalcode). */ package codes // import "go.opentelemetry.io/otel/codes" golang-opentelemetry-otel-1.1.0/doc.go000066400000000000000000000027341414226744000177210ustar00rootroot00000000000000// 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 otel provides global access to the OpenTelemetry API. The subpackages of the otel package provide an implementation of the OpenTelemetry API. The provided API is used to instrument code and measure data about that code's performance and operation. The measured data, by default, is not processed or transmitted anywhere. An implementation of the OpenTelemetry SDK, like the default SDK implementation (go.opentelemetry.io/otel/sdk), and associated exporters are used to process and transport this data. To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/. To read more about tracing, see go.opentelemetry.io/otel/trace. To read more about metrics, see go.opentelemetry.io/otel/metric. To read more about propagation, see go.opentelemetry.io/otel/propagation and go.opentelemetry.io/otel/baggage. */ package otel // import "go.opentelemetry.io/otel" golang-opentelemetry-otel-1.1.0/error_handler.go000066400000000000000000000025661414226744000220050ustar00rootroot00000000000000// 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 otel // import "go.opentelemetry.io/otel" // ErrorHandler handles irremediable events. type ErrorHandler interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Handle handles any error deemed irremediable by an OpenTelemetry // component. Handle(error) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // ErrorHandlerFunc is a convenience adapter to allow the use of a function // as an ErrorHandler. type ErrorHandlerFunc func(error) var _ ErrorHandler = ErrorHandlerFunc(nil) // Handle handles the irremediable error by calling the ErrorHandlerFunc itself. func (f ErrorHandlerFunc) Handle(err error) { f(err) } golang-opentelemetry-otel-1.1.0/example/000077500000000000000000000000001414226744000202525ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/fib/000077500000000000000000000000001414226744000210125ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/fib/app.go000066400000000000000000000050061414226744000221220ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "fmt" "io" "log" "strconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) // name is the Tracer name used to identify this instrumentation library. const name = "fib" // App is an Fibonacci computation application. type App struct { r io.Reader l *log.Logger } // NewApp returns a new App. func NewApp(r io.Reader, l *log.Logger) *App { return &App{r: r, l: l} } // Run starts polling users for Fibonacci number requests and writes results. func (a *App) Run(ctx context.Context) error { for { var span trace.Span ctx, span = otel.Tracer(name).Start(ctx, "Run") n, err := a.Poll(ctx) if err != nil { span.End() return err } a.Write(ctx, n) span.End() } } // Poll asks a user for input and returns the request. func (a *App) Poll(ctx context.Context) (uint, error) { _, span := otel.Tracer(name).Start(ctx, "Poll") defer span.End() a.l.Print("What Fibonacci number would you like to know: ") var n uint _, err := fmt.Fscanf(a.r, "%d", &n) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) return 0, err } // Store n as a string to not overflow an int64. nStr := strconv.FormatUint(uint64(n), 10) span.SetAttributes(attribute.String("request.n", nStr)) return n, nil } // Write writes the n-th Fibonacci number back to the user. func (a *App) Write(ctx context.Context, n uint) { var span trace.Span ctx, span = otel.Tracer(name).Start(ctx, "Write") defer span.End() f, err := func(ctx context.Context) (uint64, error) { _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() f, err := Fibonacci(n) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) } return f, err }(ctx) if err != nil { a.l.Printf("Fibonacci(%d): %v\n", n, err) } else { a.l.Printf("Fibonacci(%d) = %d\n", n, f) } } golang-opentelemetry-otel-1.1.0/example/fib/fib.go000066400000000000000000000016671414226744000221130ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import "fmt" // Fibonacci returns the n-th fibonacci number. func Fibonacci(n uint) (uint64, error) { if n <= 1 { return uint64(n), nil } if n > 93 { return 0, fmt.Errorf("unsupported fibonacci number %d: too large", n) } var n2, n1 uint64 = 0, 1 for i := uint(2); i < n; i++ { n2, n1 = n1, n1+n2 } return n2 + n1, nil } golang-opentelemetry-otel-1.1.0/example/fib/go.mod000066400000000000000000000052161414226744000221240ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/fib go 1.15 require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/fib => ./ replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/fib/go.sum000066400000000000000000000031151414226744000221450ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/example/fib/main.go000066400000000000000000000044631414226744000222740ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "io" "log" "os" "os/signal" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) // newExporter returns a console exporter. func newExporter(w io.Writer) (trace.SpanExporter, error) { return stdouttrace.New( stdouttrace.WithWriter(w), // Use human readable output. stdouttrace.WithPrettyPrint(), // Do not print timestamps for the demo. stdouttrace.WithoutTimestamps(), ) } // newResource returns a resource describing this application. func newResource() *resource.Resource { r, _ := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("fib"), semconv.ServiceVersionKey.String("v0.1.0"), attribute.String("environment", "demo"), ), ) return r } func main() { l := log.New(os.Stdout, "", 0) // Write telemetry data to a file. f, err := os.Create("traces.txt") if err != nil { l.Fatal(err) } defer f.Close() exp, err := newExporter(f) if err != nil { l.Fatal(err) } tp := trace.NewTracerProvider( trace.WithBatcher(exp), trace.WithResource(newResource()), ) defer func() { if err := tp.Shutdown(context.Background()); err != nil { l.Fatal(err) } }() otel.SetTracerProvider(tp) sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) errCh := make(chan error) app := NewApp(os.Stdin, l) go func() { errCh <- app.Run(context.Background()) }() select { case <-sigCh: l.Println("\ngoodbye") return case err := <-errCh: if err != nil { l.Fatal(err) } } } golang-opentelemetry-otel-1.1.0/example/jaeger/000077500000000000000000000000001414226744000215075ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/jaeger/go.mod000066400000000000000000000052361414226744000226230ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/jaeger go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/jaeger v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ./ replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/jaeger/go.sum000066400000000000000000000032351414226744000226450ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/example/jaeger/main.go000066400000000000000000000055611414226744000227710ustar00rootroot00000000000000// 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. // Command jaeger is an example program that creates spans // and uploads to Jaeger. package main import ( "context" "log" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) const ( service = "trace-demo" environment = "production" id = 1 ) // tracerProvider returns an OpenTelemetry TracerProvider configured to use // the Jaeger exporter that will send spans to the provided url. The returned // TracerProvider will also use a Resource configured with all the information // about the application. func tracerProvider(url string) (*tracesdk.TracerProvider, error) { // Create the Jaeger exporter exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) if err != nil { return nil, err } tp := tracesdk.NewTracerProvider( // Always be sure to batch in production. tracesdk.WithBatcher(exp), // Record information about this application in an Resource. tracesdk.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String(service), attribute.String("environment", environment), attribute.Int64("ID", id), )), ) return tp, nil } func main() { tp, err := tracerProvider("http://localhost:14268/api/traces") if err != nil { log.Fatal(err) } // Register our TracerProvider as the global so any imported // instrumentation in the future will default to using it. otel.SetTracerProvider(tp) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Cleanly shutdown and flush telemetry when the application exits. defer func(ctx context.Context) { // Do not make the application hang when it is shutdown. ctx, cancel = context.WithTimeout(ctx, time.Second*5) defer cancel() if err := tp.Shutdown(ctx); err != nil { log.Fatal(err) } }(ctx) tr := tp.Tracer("component-main") ctx, span := tr.Start(ctx, "foo") defer span.End() bar(ctx) } func bar(ctx context.Context) { // Use the global TracerProvider. tr := otel.Tracer("component-bar") _, span := tr.Start(ctx, "bar") span.SetAttributes(attribute.Key("testset").String("value")) defer span.End() // Do bar... } golang-opentelemetry-otel-1.1.0/example/namedtracer/000077500000000000000000000000001414226744000225375ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/namedtracer/foo/000077500000000000000000000000001414226744000233225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/namedtracer/foo/foo.go000066400000000000000000000025451414226744000244420ustar00rootroot00000000000000// 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 foo // import "go.opentelemetry.io/otel/example/namedtracer/foo" import ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) var ( lemonsKey = attribute.Key("ex.com/lemons") ) // SubOperation is an example to demonstrate the use of named tracer. // It creates a named tracer with its package path. func SubOperation(ctx context.Context) error { // Using global provider. Alternative is to have application provide a getter // for its component to get the instance of the provider. tr := otel.Tracer("example/namedtracer/foo") var span trace.Span _, span = tr.Start(ctx, "Sub operation...") defer span.End() span.SetAttributes(lemonsKey.String("five")) span.AddEvent("Sub span event") return nil } golang-opentelemetry-otel-1.1.0/example/namedtracer/go.mod000066400000000000000000000053311414226744000236470ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/namedtracer go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ./ replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/namedtracer/go.sum000066400000000000000000000031151414226744000236720ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/example/namedtracer/main.go000066400000000000000000000043111414226744000240110ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/example/namedtracer/foo" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" ) var ( fooKey = attribute.Key("ex.com/foo") barKey = attribute.Key("ex.com/bar") anotherKey = attribute.Key("ex.com/another") ) var tp *sdktrace.TracerProvider // initTracer creates and registers trace provider instance. func initTracer() { var err error exp, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { log.Panicf("failed to initialize stdouttrace exporter %v\n", err) return } bsp := sdktrace.NewBatchSpanProcessor(exp) tp = sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithSpanProcessor(bsp), ) otel.SetTracerProvider(tp) } func main() { // initialize trace provider. initTracer() // Create a named tracer with package path as its name. tracer := tp.Tracer("example/namedtracer/main") ctx := context.Background() defer func() { _ = tp.Shutdown(ctx) }() m0, _ := baggage.NewMember(string(fooKey), "foo1") m1, _ := baggage.NewMember(string(barKey), "bar1") b, _ := baggage.New(m0, m1) ctx = baggage.ContextWithBaggage(ctx, b) var span trace.Span ctx, span = tracer.Start(ctx, "operation") defer span.End() span.AddEvent("Nice operation!", trace.WithAttributes(attribute.Int("bogons", 100))) span.SetAttributes(anotherKey.String("yes")) if err := foo.SubOperation(ctx); err != nil { panic(err) } } golang-opentelemetry-otel-1.1.0/example/opencensus/000077500000000000000000000000001414226744000224345ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/opencensus/go.mod000066400000000000000000000056121414226744000235460ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/opencensus go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/bridge/opencensus v0.24.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.24.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 ) replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ./ replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/opencensus/go.sum000066400000000000000000000141501414226744000235700ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f h1:IUmbcoP9XyEXW+R9AbrZgDvaYVfTbISN92Y5RIV+Mx4= go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/example/opencensus/main.go000066400000000000000000000131121414226744000237050ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "fmt" "log" "time" "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric" "go.opencensus.io/metric/metricexport" "go.opencensus.io/metric/metricproducer" "go.opencensus.io/stats" "go.opencensus.io/stats/view" "go.opencensus.io/tag" octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/bridge/opencensus" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" otmetricexport "go.opentelemetry.io/otel/sdk/export/metric" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) var ( // instrumenttype differentiates between our gauge and view metrics. keyType = tag.MustNewKey("instrumenttype") // Counts the number of lines read in from standard input countMeasure = stats.Int64("test_count", "A count of something", stats.UnitDimensionless) countView = &view.View{ Name: "test_count", Measure: countMeasure, Description: "A count of something", Aggregation: view.Count(), TagKeys: []tag.Key{keyType}, } ) func main() { log.Println("Using OpenTelemetry stdout exporters.") traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { log.Fatal(fmt.Errorf("error creating trace exporter: %w", err)) } metricsExporter, err := stdoutmetric.New(stdoutmetric.WithPrettyPrint()) if err != nil { log.Fatal(fmt.Errorf("error creating metric exporter: %w", err)) } tracing(traceExporter) monitoring(metricsExporter) } // tracing demonstrates overriding the OpenCensus DefaultTracer to send spans // to the OpenTelemetry exporter by calling OpenCensus APIs. func tracing(otExporter sdktrace.SpanExporter) { ctx := context.Background() log.Println("Configuring OpenCensus. Not Registering any OpenCensus exporters.") octrace.ApplyConfig(octrace.Config{DefaultSampler: octrace.AlwaysSample()}) tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(otExporter)) otel.SetTracerProvider(tp) log.Println("Installing the OpenCensus bridge to make OpenCensus libraries write spans using OpenTelemetry.") tracer := tp.Tracer("simple") octrace.DefaultTracer = opencensus.NewTracer(tracer) tp.ForceFlush(ctx) log.Println("Creating OpenCensus span, which should be printed out using the OpenTelemetry stdouttrace exporter.\n-- It should have no parent, since it is the first span.") ctx, outerOCSpan := octrace.StartSpan(ctx, "OpenCensusOuterSpan") outerOCSpan.End() tp.ForceFlush(ctx) log.Println("Creating OpenTelemetry span\n-- It should have the OpenCensus span as a parent, since the OpenCensus span was written with using OpenTelemetry APIs.") ctx, otspan := tracer.Start(ctx, "OpenTelemetrySpan") otspan.End() tp.ForceFlush(ctx) log.Println("Creating OpenCensus span, which should be printed out using the OpenTelemetry stdouttrace exporter.\n-- It should have the OpenTelemetry span as a parent, since it was written using OpenTelemetry APIs") _, innerOCSpan := octrace.StartSpan(ctx, "OpenCensusInnerSpan") innerOCSpan.End() tp.ForceFlush(ctx) } // monitoring demonstrates creating an IntervalReader using the OpenTelemetry // exporter to send metrics to the exporter by using either an OpenCensus // registry or an OpenCensus view. func monitoring(otExporter otmetricexport.Exporter) { log.Println("Using the OpenTelemetry stdoutmetric exporter to export OpenCensus metrics. This allows routing telemetry from both OpenTelemetry and OpenCensus to a single exporter.") ocExporter := opencensus.NewMetricExporter(otExporter) intervalReader, err := metricexport.NewIntervalReader(&metricexport.Reader{}, ocExporter) if err != nil { log.Fatalf("Failed to create interval reader: %v\n", err) } intervalReader.ReportingInterval = 10 * time.Second log.Println("Emitting metrics using OpenCensus APIs. These should be printed out using the OpenTelemetry stdoutmetric exporter.") err = intervalReader.Start() if err != nil { log.Fatalf("Failed to start interval reader: %v\n", err) } defer intervalReader.Stop() log.Println("Registering a gauge metric using an OpenCensus registry.") r := metric.NewRegistry() metricproducer.GlobalManager().AddProducer(r) gauge, err := r.AddInt64Gauge( "test_gauge", metric.WithDescription("A gauge for testing"), metric.WithConstLabel(map[metricdata.LabelKey]metricdata.LabelValue{ {Key: keyType.Name()}: metricdata.NewLabelValue("gauge"), }), ) if err != nil { log.Fatalf("Failed to add gauge: %v\n", err) } entry, err := gauge.GetEntry() if err != nil { log.Fatalf("Failed to get gauge entry: %v\n", err) } log.Println("Registering a cumulative metric using an OpenCensus view.") if err := view.Register(countView); err != nil { log.Fatalf("Failed to register views: %v", err) } ctx, err := tag.New(context.Background(), tag.Insert(keyType, "view")) if err != nil { log.Fatalf("Failed to set tag: %v\n", err) } for i := int64(1); true; i++ { // update stats for our gauge entry.Set(i) // update stats for our view stats.Record(ctx, countMeasure.M(1)) time.Sleep(time.Second) } } golang-opentelemetry-otel-1.1.0/example/otel-collector/000077500000000000000000000000001414226744000232015ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/otel-collector/Makefile000066400000000000000000000046601414226744000246470ustar00rootroot00000000000000namespace-k8s: kubectl apply -f k8s/namespace.yaml jaeger-operator-k8s: # Create the jaeger operator and necessary artifacts in ns observability kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml # Create the cluster role & bindings kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml jaeger-k8s: kubectl apply -f k8s/jaeger.yaml prometheus-k8s: kubectl apply -f k8s/prometheus-service.yaml # Prometheus instance kubectl apply -f k8s/prometheus-monitor.yaml # Service monitor otel-collector-k8s: kubectl apply -f k8s/otel-collector.yaml clean-k8s: - kubectl delete -f k8s/otel-collector.yaml - kubectl delete -f k8s/prometheus-monitor.yaml - kubectl delete -f k8s/prometheus-service.yaml - kubectl delete -f k8s/jaeger.yaml - kubectl delete -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml - kubectl delete -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml - kubectl delete -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml golang-opentelemetry-otel-1.1.0/example/otel-collector/README.md000066400000000000000000000151621414226744000244650ustar00rootroot00000000000000# OpenTelemetry Collector Traces Example This example illustrates how to export trace and metric data from the OpenTelemetry-Go SDK to the OpenTelemetry Collector. From there, we bring the trace data to Jaeger and the metric data to Prometheus The complete flow is: ``` -----> Jaeger (trace) App + SDK ---> OpenTelemetry Collector ---| -----> Prometheus (metrics) ``` # Prerequisites You will need access to a Kubernetes cluster for this demo. We use a local instance of [microk8s](https://microk8s.io/), but please feel free to pick your favorite. If you do decide to use microk8s, please ensure that dns and storage addons are enabled ```bash microk8s enable dns storage ``` For simplicity, the demo application is not part of the k8s cluster, and will access the OpenTelemetry Collector through a NodePort on the cluster. Note that the NodePort opened by this demo is not secured. Ideally you'd want to either have your application running as part of the kubernetes cluster, or use a secured connection (NodePort/LoadBalancer with TLS or an ingress extension). # Deploying to Kubernetes All the necessary Kubernetes deployment files are available in this demo, in the [k8s](./k8s) folder. For your convenience, we assembled a [makefile](./Makefile) with deployment commands (see below). For those with subtly different systems, you are, of course, welcome to poke inside the Makefile and run the commands manually. If you use microk8s and alias `microk8s kubectl` to `kubectl`, the Makefile will not recognize the alias, and so the commands will have to be run manually. ## Setting up the Prometheus operator If you're using microk8s like us, simply do ```bash microk8s enable prometheus ``` and you're good to go. Move on to [Using the makefile](#using-the-makefile). Otherwise, obtain a copy of the Prometheus Operator stack from [coreos](https://github.com/coreos/kube-prometheus): ```bash git clone https://github.com/coreos/kube-prometheus.git cd kube-prometheus kubectl create -f manifests/setup # wait for namespaces and CRDs to become available, then kubectl create -f manifests/ ``` And to tear down the stack when you're finished: ```bash kubectl delete --ignore-not-found=true -f manifests/ -f manifests/setup ``` ## Using the makefile Next, we can deploy our Jaeger instance, Prometheus monitor, and Collector using the [makefile](./Makefile). ```bash # Create the namespace make namespace-k8s # Deploy Jaeger operator make jaeger-operator-k8s # After the operator is deployed, create the Jaeger instance make jaeger-k8s # Then the Prometheus instance. Ensure you have enabled a Prometheus operator # before executing (see above). make prometheus-k8s # Finally, deploy the OpenTelemetry Collector make otel-collector-k8s ``` If you want to clean up after this, you can use the `make clean-k8s` to delete all the resources created above. Note that this will not remove the namespace. Because Kubernetes sometimes gets stuck when removing namespaces, please remove this namespace manually after all the resources inside have been deleted, for example with ```bash kubectl delete namespaces observability ``` # Configuring the OpenTelemetry Collector Although the above steps should deploy and configure everything, let's spend some time on the [configuration](./k8s/otel-collector.yaml) of the Collector. One important part here is that, in order to enable our application to send data to the OpenTelemetry Collector, we need to first configure the `otlp` receiver: ```yml ... otel-collector-config: | receivers: # Make sure to add the otlp receiver. # This will open up the receiver on port 4317. otlp: protocols: grpc: endpoint: "0.0.0.0:4317" processors: ... ``` This will create the receiver on the Collector side, and open up port `4317` for receiving traces. The rest of the configuration is quite standard, with the only mention that we need to create the Jaeger and Prometheus exporters: ```yml ... exporters: jaeger: endpoint: "jaeger-collector.observability.svc.cluster.local:14250" prometheus: endpoint: 0.0.0.0:8889 namespace: "testapp" ... ``` ## OpenTelemetry Collector service One more aspect in the OpenTelemetry Collector [configuration](./k8s/otel-collector.yaml) worth looking at is the NodePort service used for accessing it: ```yaml apiVersion: v1 kind: Service metadata: ... spec: ports: - name: otlp # Default endpoint for otlp receiver. port: 4317 protocol: TCP targetPort: 4317 nodePort: 30080 - name: metrics # Endpoint for metrics from our app. port: 8889 protocol: TCP targetPort: 8889 selector: component: otel-collector type: NodePort ``` This service will bind the `4317` port used to access the otlp receiver to port `30080` on your cluster's node. By doing so, it makes it possible for us to access the Collector by using the static address `:30080`. In case you are running a local cluster, this will be `localhost:30080`. Note that you can also change this to a LoadBalancer or have an ingress extension for accessing the service. # Running the code You can find the complete code for this example in the [main.go](./main.go) file. To run it, ensure you have a somewhat recent version of Go (preferably >= 1.13) and do ```bash go run main.go ``` The example simulates an application, hard at work, computing for ten seconds then finishing. # Viewing instrumentation data Now the exciting part! Let's check out the telemetry data generated by our sample application ## Jaeger UI First, we need to enable an ingress provider. If you've been using microk8s, do ```bash microk8s enable ingress ``` Then find out where the Jaeger console is living: ```bash kubectl get ingress --all-namespaces ``` For us, we get the output ``` NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE observability jaeger-query * 127.0.0.1 80 5h40m ``` indicating that the Jaeger UI is available at [http://localhost:80](http://localhost:80). Navigate there in your favorite web-browser to view the generated traces. ## Prometheus Unfortunately, the Prometheus operator doesn't provide a convenient out-of-the-box ingress route for us to use, so we'll use port-forwarding instead. Note: this is a quick-and-dirty solution for the sake of example. You *will* be attacked by shady people if you do this in production! ```bash kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090 ``` Then navigate to [http://localhost:9090](http://localhost:9090) to view the Prometheus dashboard. golang-opentelemetry-otel-1.1.0/example/otel-collector/go.mod000066400000000000000000000054031414226744000243110ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/otel-collector go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 google.golang.org/grpc v1.41.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ./ replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/otel-collector/go.sum000066400000000000000000000314251414226744000243410ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/000077500000000000000000000000001414226744000237065ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/jaeger.yaml000066400000000000000000000012511414226744000260260ustar00rootroot00000000000000# 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. apiVersion: jaegertracing.io/v1 kind: Jaeger metadata: name: jaeger namespace: observabilitygolang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/namespace.yaml000066400000000000000000000012101414226744000265200ustar00rootroot00000000000000# 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. apiVersion: v1 kind: Namespace metadata: name: observability golang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/otel-collector.yaml000066400000000000000000000101511414226744000275170ustar00rootroot00000000000000# 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. apiVersion: v1 kind: ConfigMap metadata: name: otel-collector-conf namespace: observability labels: app: opentelemetry component: otel-collector-conf data: otel-collector-config: | receivers: # Make sure to add the otlp receiver. # This will open up the receiver on port 4317 otlp: protocols: grpc: endpoint: "0.0.0.0:4317" processors: extensions: health_check: {} exporters: jaeger: endpoint: "jaeger-collector.observability.svc.cluster.local:14250" insecure: true prometheus: endpoint: 0.0.0.0:8889 namespace: "testapp" logging: service: extensions: [health_check] pipelines: traces: receivers: [otlp] processors: [] exporters: [jaeger] metrics: receivers: [otlp] processors: [] exporters: [prometheus, logging] --- apiVersion: v1 kind: Service metadata: name: otel-collector namespace: observability labels: app: opentelemetry component: otel-collector spec: ports: - name: otlp # Default endpoint for otlp receiver. port: 4317 protocol: TCP targetPort: 4317 nodePort: 30080 - name: metrics # Default endpoint for metrics. port: 8889 protocol: TCP targetPort: 8889 selector: component: otel-collector type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: name: otel-collector namespace: observability labels: app: opentelemetry component: otel-collector spec: selector: matchLabels: app: opentelemetry component: otel-collector minReadySeconds: 5 progressDeadlineSeconds: 120 replicas: 1 #TODO - adjust this to your own requirements template: metadata: annotations: prometheus.io/path: "/metrics" prometheus.io/port: "8889" prometheus.io/scrape: "true" labels: app: opentelemetry component: otel-collector spec: containers: - command: - "/otelcol" - "--config=/conf/otel-collector-config.yaml" # Memory Ballast size should be max 1/3 to 1/2 of memory. - "--mem-ballast-size-mib=683" env: - name: GOGC value: "80" image: otel/opentelemetry-collector:0.6.0 name: otel-collector resources: limits: cpu: 1 memory: 2Gi requests: cpu: 200m memory: 400Mi ports: - containerPort: 4317 # Default endpoint for otlp receiver. - containerPort: 8889 # Default endpoint for querying metrics. volumeMounts: - name: otel-collector-config-vol mountPath: /conf # - name: otel-collector-secrets # mountPath: /secrets livenessProbe: httpGet: path: / port: 13133 # Health Check extension default port. readinessProbe: httpGet: path: / port: 13133 # Health Check extension default port. volumes: - configMap: name: otel-collector-conf items: - key: otel-collector-config path: otel-collector-config.yaml name: otel-collector-config-vol # - secret: # name: otel-collector-secrets # items: # - key: cert.pem # path: cert.pem # - key: key.pem # path: key.pem golang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/prometheus-monitor.yaml000066400000000000000000000023071414226744000304540ustar00rootroot00000000000000# 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. apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: app: prometheus prometheus: service-prometheus name: service-prometheus namespace: monitoring spec: alerting: alertmanagers: - name: alertmanager-main namespace: monitoring port: web baseImage: quay.io/prometheus/prometheus logLevel: info paused: false replicas: 2 retention: 2d routePrefix: / ruleSelector: matchLabels: prometheus: service-prometheus role: alert-rules serviceAccountName: prometheus-k8s serviceMonitorSelector: matchExpressions: - key: serviceapp operator: Exists golang-opentelemetry-otel-1.1.0/example/otel-collector/k8s/prometheus-service.yaml000066400000000000000000000017271414226744000304320ustar00rootroot00000000000000# 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. apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: serviceapp: otel-collector name: otel-collector namespace: observability spec: endpoints: - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token interval: 30s port: metrics namespaceSelector: matchNames: - observability selector: matchLabels: app: opentelemetry golang-opentelemetry-otel-1.1.0/example/otel-collector/main.go000066400000000000000000000073451414226744000244650ustar00rootroot00000000000000// 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. // Example using OTLP exporters + collector + third-party backends. For // information about using the exporter, see: // https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#example-package-Insecure package main import ( "context" "fmt" "log" "time" "google.golang.org/grpc" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) // Initializes an OTLP exporter, and configures the corresponding trace and // metric providers. func initProvider() func() { ctx := context.Background() res, err := resource.New(ctx, resource.WithAttributes( // the service name used to display traces in backends semconv.ServiceNameKey.String("test-service"), ), ) handleErr(err, "failed to create resource") // If the OpenTelemetry Collector is running on a local cluster (minikube or // microk8s), it should be accessible through the NodePort service at the // `localhost:30080` endpoint. Otherwise, replace `localhost` with the // endpoint of your cluster. If you run the app inside k8s, then you can // probably connect directly to the service through dns // Set up a trace exporter traceExporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint("localhost:30080"), otlptracegrpc.WithDialOption(grpc.WithBlock()), ) handleErr(err, "failed to create trace exporter") // Register the trace exporter with a TracerProvider, using a batch // span processor to aggregate spans before export. bsp := sdktrace.NewBatchSpanProcessor(traceExporter) tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) otel.SetTracerProvider(tracerProvider) // set global propagator to tracecontext (the default is no-op). otel.SetTextMapPropagator(propagation.TraceContext{}) return func() { // Shutdown will flush any remaining spans and shut down the exporter. handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider") } } func main() { log.Printf("Waiting for connection...") shutdown := initProvider() defer shutdown() tracer := otel.Tracer("test-tracer") // labels represent additional key-value descriptors that can be bound to a // metric observer or recorder. commonLabels := []attribute.KeyValue{ attribute.String("labelA", "chocolate"), attribute.String("labelB", "raspberry"), attribute.String("labelC", "vanilla"), } // work begins ctx, span := tracer.Start( context.Background(), "CollectorExporter-Example", trace.WithAttributes(commonLabels...)) defer span.End() for i := 0; i < 10; i++ { _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) log.Printf("Doing really hard work (%d / 10)\n", i+1) <-time.After(time.Second) iSpan.End() } log.Printf("Done!") } func handleErr(err error, message string) { if err != nil { log.Fatalf("%s: %v", message, err) } } golang-opentelemetry-otel-1.1.0/example/passthrough/000077500000000000000000000000001414226744000226215ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/passthrough/README.md000066400000000000000000000030251414226744000241000ustar00rootroot00000000000000# "Passthrough" setup for OpenTelemetry Some Go programs may wish to propagate context without recording spans. To do this in OpenTelemetry, simply install `TextMapPropagators`, but do not install a TracerProvider using the SDK. This works because the default TracerProvider implementation returns a "Non-Recording" span that keeps the context of the caller but does not record spans. For example, when you initialize your global settings, the following will propagate context without recording spans: ```golang // Setup Propagators only otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) ``` But the following will propagate context _and_ create new, potentially recorded spans: ```golang // Setup SDK exp, _ := stdout.New(stdout.WithPrettyPrint()) tp = sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), ) otel.SetTracerProvider(tp) // Setup Propagators otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) ``` ## The Demo The demo has the following call structure: `Outer -> Passthrough -> Inner` If all components had both an SDK and propagators registered, we would expect the trace to look like: ``` |-------outer---------| |-Passthrough recv-| |Passthrough send| |---inner---| ``` However, in this demo, only the outer and inner have TracerProvider backed by the SDK. All components have Propagators set. In this case, we expect to see: ``` |-------outer---------| |---inner---| ``` golang-opentelemetry-otel-1.1.0/example/passthrough/go.mod000066400000000000000000000054201414226744000237300ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/passthrough go 1.15 require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/sdk => ../../sdk go.opentelemetry.io/otel/trace => ../../trace ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/passthrough => ./ replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/sdk/trace => ../../sdk/trace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/passthrough/go.sum000066400000000000000000000031151414226744000237540ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/example/passthrough/handler/000077500000000000000000000000001414226744000242365ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/passthrough/handler/handler.go000066400000000000000000000051001414226744000261760ustar00rootroot00000000000000// 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 handler // import "go.opentelemetry.io/otel/example/passthrough/handler" import ( "context" "log" "net/http" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) // Handler is a minimal implementation of the handler and client from // go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp for demonstration purposes. // It handles an incoming http request, and makes an outgoing http request. type Handler struct { propagators propagation.TextMapPropagator tracer trace.Tracer next func(r *http.Request) } func New(next func(r *http.Request)) *Handler { // Like most instrumentation packages, this handler defaults to using the // global progatators and tracer providers. return &Handler{ propagators: otel.GetTextMapPropagator(), tracer: otel.Tracer("examples/passthrough/handler"), next: next, } } // HandleHTTPReq mimics what an instrumented http server does func (h *Handler) HandleHTTPReq(r *http.Request) { ctx := h.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) var span trace.Span log.Println("The \"handle passthrough request\" span should NOT be recorded, because it is recorded by a TracerProvider not backed by the SDK.") ctx, span = h.tracer.Start(ctx, "handle passthrough request") defer span.End() // Pretend to do work time.Sleep(time.Second) h.makeOutgoingRequest(ctx) } // makeOutgoingRequest mimics what an instrumented http client does func (h *Handler) makeOutgoingRequest(ctx context.Context) { // make a new http request r, err := http.NewRequest("", "", nil) if err != nil { panic(err) } log.Println("The \"make outgoing request from passthrough\" span should NOT be recorded, because it is recorded by a TracerProvider not backed by the SDK.") ctx, span := h.tracer.Start(ctx, "make outgoing request from passthrough") defer span.End() r = r.WithContext(ctx) h.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header)) h.next(r) } golang-opentelemetry-otel-1.1.0/example/passthrough/main.go000066400000000000000000000064071414226744000241030ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "log" "net/http" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/example/passthrough/handler" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" ) func main() { ctx := context.Background() initPassthroughGlobals() tp := nonGlobalTracer() defer func() { _ = tp.Shutdown(ctx) }() // make an initial http request r, err := http.NewRequest("", "", nil) if err != nil { panic(err) } // This is roughly what an instrumented http client does. log.Println("The \"make outer request\" span should be recorded, because it is recorded with a Tracer from the SDK TracerProvider") var span trace.Span ctx, span = tp.Tracer("example/passthrough/outer").Start(ctx, "make outer request") defer span.End() r = r.WithContext(ctx) otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header)) backendFunc := func(r *http.Request) { // This is roughly what an instrumented http server does. ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)) log.Println("The \"handle inner request\" span should be recorded, because it is recorded with a Tracer from the SDK TracerProvider") _, span := tp.Tracer("example/passthrough/inner").Start(ctx, "handle inner request") defer span.End() // Do "backend work" time.Sleep(time.Second) } // This handler will be a passthrough, since we didn't set a global TracerProvider passthroughHandler := handler.New(backendFunc) passthroughHandler.HandleHTTPReq(r) } func initPassthroughGlobals() { // We explicitly DO NOT set the global TracerProvider using otel.SetTracerProvider(). // The unset TracerProvider returns a "non-recording" span, but still passes through context. log.Println("Register a global TextMapPropagator, but do not register a global TracerProvider to be in \"passthrough\" mode.") log.Println("The \"passthrough\" mode propagates the TraceContext and Baggage, but does not record spans.") otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) } // nonGlobalTracer creates a trace provider instance for testing, but doesn't // set it as the global tracer provider func nonGlobalTracer() *sdktrace.TracerProvider { var err error exp, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { log.Panicf("failed to initialize stdouttrace exporter %v\n", err) } bsp := sdktrace.NewBatchSpanProcessor(exp) tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithSpanProcessor(bsp), ) return tp } golang-opentelemetry-otel-1.1.0/example/prometheus/000077500000000000000000000000001414226744000224455ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/prometheus/go.mod000066400000000000000000000054101414226744000235530ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/prometheus go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/prometheus v0.24.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ./ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/prometheus/go.sum000066400000000000000000000345351414226744000236120ustar00rootroot00000000000000cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/example/prometheus/main.go000066400000000000000000000070121414226744000237200ustar00rootroot00000000000000// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "fmt" "log" "net/http" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) var ( lemonsKey = attribute.Key("ex.com/lemons") ) func initMeter() { config := prometheus.Config{} c := controller.New( processor.NewFactory( selector.NewWithHistogramDistribution( histogram.WithExplicitBoundaries(config.DefaultHistogramBoundaries), ), aggregation.CumulativeTemporalitySelector(), processor.WithMemory(true), ), ) exporter, err := prometheus.New(config, c) if err != nil { log.Panicf("failed to initialize prometheus exporter %v", err) } global.SetMeterProvider(exporter.MeterProvider()) http.HandleFunc("/", exporter.ServeHTTP) go func() { _ = http.ListenAndServe(":2222", nil) }() fmt.Println("Prometheus server running on :2222") } func main() { initMeter() meter := global.Meter("ex.com/basic") observerLock := new(sync.RWMutex) observerValueToReport := new(float64) observerLabelsToReport := new([]attribute.KeyValue) cb := func(_ context.Context, result metric.Float64ObserverResult) { (*observerLock).RLock() value := *observerValueToReport labels := *observerLabelsToReport (*observerLock).RUnlock() result.Observe(value, labels...) } _ = metric.Must(meter).NewFloat64GaugeObserver("ex.com.one", cb, metric.WithDescription("A GaugeObserver set to 1.0"), ) histogram := metric.Must(meter).NewFloat64Histogram("ex.com.two") counter := metric.Must(meter).NewFloat64Counter("ex.com.three") commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")} notSoCommonLabels := []attribute.KeyValue{lemonsKey.Int(13)} ctx := context.Background() (*observerLock).Lock() *observerValueToReport = 1.0 *observerLabelsToReport = commonLabels (*observerLock).Unlock() meter.RecordBatch( ctx, commonLabels, histogram.Measurement(2.0), counter.Measurement(12.0), ) time.Sleep(5 * time.Second) (*observerLock).Lock() *observerValueToReport = 1.0 *observerLabelsToReport = notSoCommonLabels (*observerLock).Unlock() meter.RecordBatch( ctx, notSoCommonLabels, histogram.Measurement(2.0), counter.Measurement(22.0), ) time.Sleep(5 * time.Second) (*observerLock).Lock() *observerValueToReport = 13.0 *observerLabelsToReport = commonLabels (*observerLock).Unlock() meter.RecordBatch( ctx, commonLabels, histogram.Measurement(12.0), counter.Measurement(13.0), ) fmt.Println("Example finished updating, please visit :2222") select {} } golang-opentelemetry-otel-1.1.0/example/zipkin/000077500000000000000000000000001414226744000215565ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/example/zipkin/Dockerfile000066400000000000000000000014241414226744000235510ustar00rootroot00000000000000# 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. FROM golang:1.16-alpine COPY . /go/src/github.com/open-telemetry/opentelemetry-go/ WORKDIR /go/src/github.com/open-telemetry/opentelemetry-go/example/zipkin/ RUN go install ./main.go CMD ["/go/bin/main"] golang-opentelemetry-otel-1.1.0/example/zipkin/README.md000066400000000000000000000017211414226744000230360ustar00rootroot00000000000000# Zipkin Exporter Example Send an example span to a [Zipkin](https://zipkin.io/) service. These instructions expect you have [docker-compose](https://docs.docker.com/compose/) installed. Bring up the `zipkin-collector` service and example `zipkin-client` service to send an example trace: ```sh docker-compose up --detach zipkin-collector zipkin-client ``` The `zipkin-client` service sends just one trace and exits. Retrieve the `traceId` generated by the `zipkin-client` service; should be the last line in the logs: ```sh docker-compose logs --tail=1 zipkin-client ``` With the `traceId` you can view the trace from the `zipkin-collector` service UI hosted on port `9411`, e.g. with `traceId` of `f5695ba3b2ed00ea583fa4fa0badbeef`: [http://localhost:9411/zipkin/traces/f5695ba3b2ed00ea583fa4fa0badbeef](http://localhost:9411/zipkin/traces/f5695ba3b2ed00ea583fa4fa0badbeef) Shut down the services when you are finished with the example: ```sh docker-compose down ``` golang-opentelemetry-otel-1.1.0/example/zipkin/docker-compose.yml000066400000000000000000000022011414226744000252060ustar00rootroot00000000000000# 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. version: "3.7" services: zipkin-collector: image: openzipkin/zipkin-slim:latest ports: - "9411:9411" networks: - example zipkin-client: build: dockerfile: $PWD/Dockerfile context: ../.. command: - "/bin/sh" - "-c" - "while ! nc -w 1 -z zipkin-collector 9411; do echo sleep for 1s waiting for zipkin-collector to become available; sleep 1; done && /go/bin/main -zipkin http://zipkin-collector:9411/api/v2/spans" networks: - example depends_on: - zipkin-collector networks: example: golang-opentelemetry-otel-1.1.0/example/zipkin/go.mod000066400000000000000000000053051414226744000226670ustar00rootroot00000000000000module go.opentelemetry.io/otel/example/zipkin go 1.15 replace ( go.opentelemetry.io/otel => ../.. go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/zipkin v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../prometheus replace go.opentelemetry.io/otel/example/zipkin => ./ replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/example/zipkin/go.sum000066400000000000000000000252761414226744000227250ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/example/zipkin/main.go000066400000000000000000000047421414226744000230400ustar00rootroot00000000000000// 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. // Command zipkin is an example program that creates spans // and uploads to openzipkin collector. package main import ( "context" "flag" "log" "os" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/zipkin" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) var logger = log.New(os.Stderr, "zipkin-example", log.Ldate|log.Ltime|log.Llongfile) // initTracer creates a new trace provider instance and registers it as global trace provider. func initTracer(url string) func() { // Create Zipkin Exporter and install it as a global tracer. // // For demoing purposes, always sample. In a production application, you should // configure the sampler to a trace.ParentBased(trace.TraceIDRatioBased) set at the desired // ratio. exporter, err := zipkin.New( url, zipkin.WithLogger(logger), ) if err != nil { log.Fatal(err) } batcher := sdktrace.NewBatchSpanProcessor(exporter) tp := sdktrace.NewTracerProvider( sdktrace.WithSpanProcessor(batcher), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("zipkin-test"), )), ) otel.SetTracerProvider(tp) return func() { _ = tp.Shutdown(context.Background()) } } func main() { url := flag.String("zipkin", "http://localhost:9411/api/v2/spans", "zipkin url") flag.Parse() shutdown := initTracer(*url) defer shutdown() ctx := context.Background() tr := otel.GetTracerProvider().Tracer("component-main") ctx, span := tr.Start(ctx, "foo", trace.WithSpanKind(trace.SpanKindServer)) <-time.After(6 * time.Millisecond) bar(ctx) <-time.After(6 * time.Millisecond) span.End() } func bar(ctx context.Context) { tr := otel.GetTracerProvider().Tracer("component-bar") _, span := tr.Start(ctx, "bar") <-time.After(6 * time.Millisecond) span.End() } golang-opentelemetry-otel-1.1.0/exporters/000077500000000000000000000000001414226744000206525ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/000077500000000000000000000000001414226744000221075ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/README.md000066400000000000000000000055741414226744000234010ustar00rootroot00000000000000# OpenTelemetry-Go Jaeger Exporter [![Go Reference](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/jaeger.svg)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger) [OpenTelemetry span exporter for Jaeger](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md) implementation. ## Installation ``` go get -u go.opentelemetry.io/otel/exporters/jaeger ``` ## Example See [../../example/jaeger](../../example/jaeger). ## Configuration The exporter can be used to send spans to: - Jaeger agent using `jaeger.thrift` over compact thrift protocol via [`WithAgentEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentEndpoint) option. - Jaeger collector using `jaeger.thrift` over HTTP via [`WithCollectorEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithCollectorEndpoint) option. ### Environment Variables The following environment variables can be used (instead of options objects) to override the default configuration. | Environment variable | Option | Default value | | --------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------- | | `OTEL_EXPORTER_JAEGER_AGENT_HOST` | [`WithAgentHost`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentHost) | `localhost` | | `OTEL_EXPORTER_JAEGER_AGENT_PORT` | [`WithAgentPort`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentPort) | `6831` | | `OTEL_EXPORTER_JAEGER_ENDPOINT` | [`WithEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithEndpoint) | `http://localhost:14268/api/traces` | | `OTEL_EXPORTER_JAEGER_USER` | [`WithUsername`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithUsername) | | | `OTEL_EXPORTER_JAEGER_PASSWORD` | [`WithPassword`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithPassword) | | Configuration using options have precedence over the environment variables. ## Contributing This exporter uses a vendored copy of the Apache Thrift library (v0.14.1) at a custom import path. When re-generating Thrift code in the future, please adapt import paths as necessary. ## References - [Jaeger](https://www.jaegertracing.io/) - [OpenTelemetry to Jaeger Transformation](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md) - [OpenTelemetry Environment Variable Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md) golang-opentelemetry-otel-1.1.0/exporters/jaeger/agent.go000066400000000000000000000140031414226744000235320ustar00rootroot00000000000000// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" import ( "context" "fmt" "io" "log" "net" "strings" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" genAgent "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent" gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" ) // udpPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent const udpPacketMaxLength = 65000 // agentClientUDP is a UDP client to Jaeger agent that implements gen.Agent interface. type agentClientUDP struct { genAgent.Agent io.Closer connUDP udpConn client *genAgent.AgentClient maxPacketSize int // max size of datagram in bytes thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span thriftProtocol thrift.TProtocol } type udpConn interface { Write([]byte) (int, error) SetWriteBuffer(int) error Close() error } type agentClientUDPParams struct { Host string Port string MaxPacketSize int Logger *log.Logger AttemptReconnecting bool AttemptReconnectInterval time.Duration } // newAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP. func newAgentClientUDP(params agentClientUDPParams) (*agentClientUDP, error) { hostPort := net.JoinHostPort(params.Host, params.Port) // validate hostport if _, _, err := net.SplitHostPort(hostPort); err != nil { return nil, err } if params.MaxPacketSize <= 0 { params.MaxPacketSize = udpPacketMaxLength } if params.AttemptReconnecting && params.AttemptReconnectInterval <= 0 { params.AttemptReconnectInterval = time.Second * 30 } thriftBuffer := thrift.NewTMemoryBufferLen(params.MaxPacketSize) protocolFactory := thrift.NewTCompactProtocolFactoryConf(&thrift.TConfiguration{}) thriftProtocol := protocolFactory.GetProtocol(thriftBuffer) client := genAgent.NewAgentClientFactory(thriftBuffer, protocolFactory) var connUDP udpConn var err error if params.AttemptReconnecting { // host is hostname, setup resolver loop in case host record changes during operation connUDP, err = newReconnectingUDPConn(hostPort, params.MaxPacketSize, params.AttemptReconnectInterval, net.ResolveUDPAddr, net.DialUDP, params.Logger) if err != nil { return nil, err } } else { destAddr, err := net.ResolveUDPAddr("udp", hostPort) if err != nil { return nil, err } connUDP, err = net.DialUDP(destAddr.Network(), nil, destAddr) if err != nil { return nil, err } } if err := connUDP.SetWriteBuffer(params.MaxPacketSize); err != nil { return nil, err } return &agentClientUDP{ connUDP: connUDP, client: client, maxPacketSize: params.MaxPacketSize, thriftBuffer: thriftBuffer, thriftProtocol: thriftProtocol, }, nil } // EmitBatch buffers batch to fit into UDP packets and sends the data to the agent. func (a *agentClientUDP) EmitBatch(ctx context.Context, batch *gen.Batch) error { var errs []error processSize, err := a.calcSizeOfSerializedThrift(ctx, batch.Process) if err != nil { // drop the batch if serialization of process fails. return err } totalSize := processSize var spans []*gen.Span for _, span := range batch.Spans { spanSize, err := a.calcSizeOfSerializedThrift(ctx, span) if err != nil { errs = append(errs, fmt.Errorf("thrift serialization failed: %v", span)) continue } if spanSize+processSize >= a.maxPacketSize { // drop the span that exceeds the limit. errs = append(errs, fmt.Errorf("span too large to send: %v", span)) continue } if totalSize+spanSize >= a.maxPacketSize { if err := a.flush(ctx, &gen.Batch{ Process: batch.Process, Spans: spans, }); err != nil { errs = append(errs, err) } spans = spans[:0] totalSize = processSize } totalSize += spanSize spans = append(spans, span) } if len(spans) > 0 { if err := a.flush(ctx, &gen.Batch{ Process: batch.Process, Spans: spans, }); err != nil { errs = append(errs, err) } } if len(errs) == 1 { return errs[0] } else if len(errs) > 1 { joined := a.makeJoinedErrorString(errs) return fmt.Errorf("multiple errors during transform: %s", joined) } return nil } // makeJoinedErrorString join all the errors to one error message. func (a *agentClientUDP) makeJoinedErrorString(errs []error) string { var errMsgs []string for _, err := range errs { errMsgs = append(errMsgs, err.Error()) } return strings.Join(errMsgs, ", ") } // flush will send the batch of spans to the agent. func (a *agentClientUDP) flush(ctx context.Context, batch *gen.Batch) error { a.thriftBuffer.Reset() if err := a.client.EmitBatch(ctx, batch); err != nil { return err } if a.thriftBuffer.Len() > a.maxPacketSize { return fmt.Errorf("data does not fit within one UDP packet; size %d, max %d, spans %d", a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans)) } _, err := a.connUDP.Write(a.thriftBuffer.Bytes()) return err } // calcSizeOfSerializedThrift calculate the serialized thrift packet size. func (a *agentClientUDP) calcSizeOfSerializedThrift(ctx context.Context, thriftStruct thrift.TStruct) (int, error) { a.thriftBuffer.Reset() err := thriftStruct.Write(ctx, a.thriftProtocol) return a.thriftBuffer.Len(), err } // Close implements Close() of io.Closer and closes the underlying UDP connection. func (a *agentClientUDP) Close() error { return a.connUDP.Close() } golang-opentelemetry-otel-1.1.0/exporters/jaeger/agent_test.go000066400000000000000000000127611414226744000246020ustar00rootroot00000000000000// 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 jaeger import ( "context" "log" "net" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/sdk/trace/tracetest" ) func TestNewAgentClientUDPWithParamsBadHostport(t *testing.T) { agentClient, err := newAgentClientUDP(agentClientUDPParams{ Host: "blahblah", Port: "", }) assert.Error(t, err) assert.Nil(t, agentClient) } func TestNewAgentClientUDPWithParams(t *testing.T) { mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) agentClient, err := newAgentClientUDP(agentClientUDPParams{ Host: host, Port: port, MaxPacketSize: 25000, AttemptReconnecting: true, }) assert.NoError(t, err) assert.NotNil(t, agentClient) assert.Equal(t, 25000, agentClient.maxPacketSize) if assert.IsType(t, &reconnectingUDPConn{}, agentClient.connUDP) { assert.Equal(t, (*log.Logger)(nil), agentClient.connUDP.(*reconnectingUDPConn).logger) } assert.NoError(t, agentClient.Close()) } func TestNewAgentClientUDPWithParamsDefaults(t *testing.T) { mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) agentClient, err := newAgentClientUDP(agentClientUDPParams{ Host: host, Port: port, AttemptReconnecting: true, }) assert.NoError(t, err) assert.NotNil(t, agentClient) assert.Equal(t, udpPacketMaxLength, agentClient.maxPacketSize) if assert.IsType(t, &reconnectingUDPConn{}, agentClient.connUDP) { assert.Equal(t, (*log.Logger)(nil), agentClient.connUDP.(*reconnectingUDPConn).logger) } assert.NoError(t, agentClient.Close()) } func TestNewAgentClientUDPWithParamsReconnectingDisabled(t *testing.T) { mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) agentClient, err := newAgentClientUDP(agentClientUDPParams{ Host: host, Port: port, Logger: nil, AttemptReconnecting: false, }) assert.NoError(t, err) assert.NotNil(t, agentClient) assert.Equal(t, udpPacketMaxLength, agentClient.maxPacketSize) assert.IsType(t, &net.UDPConn{}, agentClient.connUDP) assert.NoError(t, agentClient.Close()) } type errorHandler struct{ t *testing.T } func (eh errorHandler) Handle(err error) { assert.NoError(eh.t, err) } func TestJaegerAgentUDPLimitBatching(t *testing.T) { otel.SetErrorHandler(errorHandler{t}) mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) // 1500 spans, size 79559, does not fit within one UDP packet with the default size of 65000. n := 1500 s := make(tracetest.SpanStubs, n).Snapshots() exp, err := New( WithAgentEndpoint(WithAgentHost(host), WithAgentPort(port)), ) require.NoError(t, err) ctx := context.Background() assert.NoError(t, exp.ExportSpans(ctx, s)) assert.NoError(t, exp.Shutdown(ctx)) } // generateALargeSpan generates a span with a long name. func generateALargeSpan() tracetest.SpanStub { return tracetest.SpanStub{ Name: "a-longer-name-that-makes-it-exceeds-limit", } } func TestSpanExceedsMaxPacketLimit(t *testing.T) { otel.SetErrorHandler(errorHandler{t}) mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) // 106 is the serialized size of a span with default values. maxSize := 106 largeSpans := tracetest.SpanStubs{generateALargeSpan(), {}}.Snapshots() normalSpans := tracetest.SpanStubs{{}, {}}.Snapshots() exp, err := New( WithAgentEndpoint(WithAgentHost(host), WithAgentPort(port), WithMaxPacketSize(maxSize+1)), ) require.NoError(t, err) ctx := context.Background() assert.Error(t, exp.ExportSpans(ctx, largeSpans)) assert.NoError(t, exp.ExportSpans(ctx, normalSpans)) assert.NoError(t, exp.Shutdown(ctx)) } func TestEmitBatchWithMultipleErrors(t *testing.T) { otel.SetErrorHandler(errorHandler{t}) mockServer, err := newUDPListener() require.NoError(t, err) defer mockServer.Close() host, port, err := net.SplitHostPort(mockServer.LocalAddr().String()) assert.NoError(t, err) span := generateALargeSpan() largeSpans := tracetest.SpanStubs{span, span}.Snapshots() // make max packet size smaller than span maxSize := len(span.Name) exp, err := New( WithAgentEndpoint(WithAgentHost(host), WithAgentPort(port), WithMaxPacketSize(maxSize)), ) require.NoError(t, err) ctx := context.Background() err = exp.ExportSpans(ctx, largeSpans) assert.Error(t, err) require.Contains(t, err.Error(), "multiple errors") } golang-opentelemetry-otel-1.1.0/exporters/jaeger/assertsocketbuffersize_test.go000066400000000000000000000026161414226744000303010ustar00rootroot00000000000000// 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. //go:build !windows // +build !windows package jaeger import ( "net" "runtime" "syscall" "testing" "github.com/stretchr/testify/assert" ) func assertSockBufferSize(t *testing.T, expectedBytes int, conn *net.UDPConn) bool { fd, err := conn.File() if !assert.NoError(t, err) { return false } bufferBytes, err := syscall.GetsockoptInt(int(fd.Fd()), syscall.SOL_SOCKET, syscall.SO_SNDBUF) if !assert.NoError(t, err) { return false } // The linux kernel doubles SO_SNDBUF value (to allow space for // bookkeeping overhead) when it is set using setsockopt(2), and this // doubled value is returned by getsockopt(2) // https://linux.die.net/man/7/socket if runtime.GOOS == "linux" { return assert.GreaterOrEqual(t, expectedBytes*2, bufferBytes) } return assert.Equal(t, expectedBytes, bufferBytes) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/assertsocketbuffersize_windows_test.go000066400000000000000000000021751414226744000320530ustar00rootroot00000000000000// 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. // +build windows package jaeger import ( "net" "testing" ) func assertSockBufferSize(t *testing.T, expectedBytes int, conn *net.UDPConn) bool { // The Windows implementation of the net.UDPConn does not implement the // functionality to return a file handle, instead a "not supported" error // is returned: // // https://github.com/golang/go/blob/6cc8aa7ece96aca282db19f08aa5c98ed13695d9/src/net/fd_windows.go#L175-L178 // // This means we are not able to pass the connection to a syscall and // determine the buffer size. return true } golang-opentelemetry-otel-1.1.0/exporters/jaeger/doc.go000066400000000000000000000013441414226744000232050ustar00rootroot00000000000000// 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 jaeger contains an OpenTelemetry tracing exporter for Jaeger. package jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" golang-opentelemetry-otel-1.1.0/exporters/jaeger/env.go000066400000000000000000000031451414226744000232310ustar00rootroot00000000000000// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" import ( "os" ) // Environment variable names const ( // Hostname for the Jaeger agent, part of address where exporter sends spans // i.e. "localhost" envAgentHost = "OTEL_EXPORTER_JAEGER_AGENT_HOST" // Port for the Jaeger agent, part of address where exporter sends spans // i.e. 6831 envAgentPort = "OTEL_EXPORTER_JAEGER_AGENT_PORT" // The HTTP endpoint for sending spans directly to a collector, // i.e. http://jaeger-collector:14268/api/traces. envEndpoint = "OTEL_EXPORTER_JAEGER_ENDPOINT" // Username to send as part of "Basic" authentication to the collector endpoint. envUser = "OTEL_EXPORTER_JAEGER_USER" // Password to send as part of "Basic" authentication to the collector endpoint. envPassword = "OTEL_EXPORTER_JAEGER_PASSWORD" ) // envOr returns an env variable's value if it is exists or the default if not func envOr(key, defaultValue string) string { if v, ok := os.LookupEnv(key); ok && v != "" { return v } return defaultValue } golang-opentelemetry-otel-1.1.0/exporters/jaeger/env_test.go000066400000000000000000000156601414226744000242750ustar00rootroot00000000000000// 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 jaeger import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ottest "go.opentelemetry.io/otel/internal/internaltest" ) func TestNewRawExporterWithDefault(t *testing.T) { const ( collectorEndpoint = "http://localhost:14268/api/traces" username = "" password = "" ) // Create Jaeger Exporter with default values exp, err := New( WithCollectorEndpoint(), ) assert.NoError(t, err) require.IsType(t, &collectorUploader{}, exp.uploader) uploader := exp.uploader.(*collectorUploader) assert.Equal(t, collectorEndpoint, uploader.endpoint) assert.Equal(t, username, uploader.username) assert.Equal(t, password, uploader.password) } func TestNewRawExporterWithEnv(t *testing.T) { const ( collectorEndpoint = "http://localhost" username = "user" password = "password" ) envStore, err := ottest.SetEnvVariables(map[string]string{ envEndpoint: collectorEndpoint, envUser: username, envPassword: password, }) require.NoError(t, err) defer func() { require.NoError(t, envStore.Restore()) }() // Create Jaeger Exporter with environment variables exp, err := New( WithCollectorEndpoint(), ) assert.NoError(t, err) require.IsType(t, &collectorUploader{}, exp.uploader) uploader := exp.uploader.(*collectorUploader) assert.Equal(t, collectorEndpoint, uploader.endpoint) assert.Equal(t, username, uploader.username) assert.Equal(t, password, uploader.password) } func TestNewRawExporterWithPassedOption(t *testing.T) { const ( collectorEndpoint = "http://localhost" username = "user" password = "password" optionEndpoint = "should not be overwritten" ) envStore, err := ottest.SetEnvVariables(map[string]string{ envEndpoint: collectorEndpoint, envUser: username, envPassword: password, }) require.NoError(t, err) defer func() { require.NoError(t, envStore.Restore()) }() // Create Jaeger Exporter with passed endpoint option, should be used over envEndpoint exp, err := New( WithCollectorEndpoint(WithEndpoint(optionEndpoint)), ) assert.NoError(t, err) require.IsType(t, &collectorUploader{}, exp.uploader) uploader := exp.uploader.(*collectorUploader) assert.Equal(t, optionEndpoint, uploader.endpoint) assert.Equal(t, username, uploader.username) assert.Equal(t, password, uploader.password) } func TestEnvOrWithAgentHostPortFromEnv(t *testing.T) { testCases := []struct { name string envAgentHost string envAgentPort string defaultHost string defaultPort string expectedHost string expectedPort string }{ { name: "overrides default host/port values via environment variables", envAgentHost: "localhost", envAgentPort: "6832", defaultHost: "hostNameToBeReplaced", defaultPort: "8203", expectedHost: "localhost", expectedPort: "6832", }, { name: "envAgentHost is empty, will not overwrite default host value", envAgentHost: "", envAgentPort: "6832", defaultHost: "hostNameNotToBeReplaced", defaultPort: "8203", expectedHost: "hostNameNotToBeReplaced", expectedPort: "6832", }, { name: "envAgentPort is empty, will not overwrite default port value", envAgentHost: "localhost", envAgentPort: "", defaultHost: "hostNameToBeReplaced", defaultPort: "8203", expectedHost: "localhost", expectedPort: "8203", }, { name: "envAgentHost and envAgentPort are empty, will not overwrite default host/port values", envAgentHost: "", envAgentPort: "", defaultHost: "hostNameNotToBeReplaced", defaultPort: "8203", expectedHost: "hostNameNotToBeReplaced", expectedPort: "8203", }, } envStore := ottest.NewEnvStore() envStore.Record(envAgentHost) envStore.Record(envAgentPort) defer func() { require.NoError(t, envStore.Restore()) }() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { require.NoError(t, os.Setenv(envAgentHost, tc.envAgentHost)) require.NoError(t, os.Setenv(envAgentPort, tc.envAgentPort)) host := envOr(envAgentHost, tc.defaultHost) port := envOr(envAgentPort, tc.defaultPort) assert.Equal(t, tc.expectedHost, host) assert.Equal(t, tc.expectedPort, port) }) } } func TestEnvOrWithCollectorEndpointOptionsFromEnv(t *testing.T) { testCases := []struct { name string envEndpoint string envUsername string envPassword string defaultCollectorEndpointOptions collectorEndpointConfig expectedCollectorEndpointOptions collectorEndpointConfig }{ { name: "overrides value via environment variables", envEndpoint: "http://localhost:14252", envUsername: "username", envPassword: "password", defaultCollectorEndpointOptions: collectorEndpointConfig{ endpoint: "endpoint not to be used", username: "foo", password: "bar", }, expectedCollectorEndpointOptions: collectorEndpointConfig{ endpoint: "http://localhost:14252", username: "username", password: "password", }, }, { name: "environment variables is empty, will not overwrite value", envEndpoint: "", envUsername: "", envPassword: "", defaultCollectorEndpointOptions: collectorEndpointConfig{ endpoint: "endpoint to be used", username: "foo", password: "bar", }, expectedCollectorEndpointOptions: collectorEndpointConfig{ endpoint: "endpoint to be used", username: "foo", password: "bar", }, }, } envStore := ottest.NewEnvStore() envStore.Record(envEndpoint) envStore.Record(envUser) envStore.Record(envPassword) defer func() { require.NoError(t, envStore.Restore()) }() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { require.NoError(t, os.Setenv(envEndpoint, tc.envEndpoint)) require.NoError(t, os.Setenv(envUser, tc.envUsername)) require.NoError(t, os.Setenv(envPassword, tc.envPassword)) endpoint := envOr(envEndpoint, tc.defaultCollectorEndpointOptions.endpoint) username := envOr(envUser, tc.defaultCollectorEndpointOptions.username) password := envOr(envPassword, tc.defaultCollectorEndpointOptions.password) assert.Equal(t, tc.expectedCollectorEndpointOptions.endpoint, endpoint) assert.Equal(t, tc.expectedCollectorEndpointOptions.username, username) assert.Equal(t, tc.expectedCollectorEndpointOptions.password, password) }) } } golang-opentelemetry-otel-1.1.0/exporters/jaeger/go.mod000066400000000000000000000053651414226744000232260ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/jaeger go 1.15 require ( github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../prometheus replace go.opentelemetry.io/otel/exporters/otlp => ../otlp replace go.opentelemetry.io/otel/exporters/jaeger => ./ replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/exporters/zipkin => ../zipkin replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/exporters/jaeger/go.sum000066400000000000000000000032351414226744000232450ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/000077500000000000000000000000001414226744000237235ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/000077500000000000000000000000001414226744000250775ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/000077500000000000000000000000001414226744000261755ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/GoUnusedProtection__.go000066400000000000000000000001531414226744000326210ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package agent var GoUnusedProtection__ int; golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/agent-consts.go000066400000000000000000000011541414226744000311320ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package agent import ( "bytes" "context" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal var _ = jaeger.GoUnusedProtection__ var _ = zipkincore.GoUnusedProtection__ func init() { } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/agent-remote/000077500000000000000000000000001414226744000305645ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/agent-remote/agent-remote.go000077500000000000000000000127741414226744000335200ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package main import ( "context" "flag" "fmt" "math" "net" "net/url" "os" "strconv" "strings" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) var _ = jaeger.GoUnusedProtection__ var _ = zipkincore.GoUnusedProtection__ var _ = agent.GoUnusedProtection__ func Usage() { fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\nFunctions:") fmt.Fprintln(os.Stderr, " void emitZipkinBatch( spans)") fmt.Fprintln(os.Stderr, " void emitBatch(Batch batch)") fmt.Fprintln(os.Stderr) os.Exit(0) } type httpHeaders map[string]string func (h httpHeaders) String() string { var m map[string]string = h return fmt.Sprintf("%s", m) } func (h httpHeaders) Set(value string) error { parts := strings.Split(value, ": ") if len(parts) != 2 { return fmt.Errorf("header should be of format 'Key: Value'") } h[parts[0]] = parts[1] return nil } func main() { flag.Usage = Usage var host string var port int var protocol string var urlString string var framed bool var useHttp bool headers := make(httpHeaders) var parsedUrl *url.URL var trans thrift.TTransport _ = strconv.Atoi _ = math.Abs flag.Usage = Usage flag.StringVar(&host, "h", "localhost", "Specify host and port") flag.IntVar(&port, "p", 9090, "Specify port") flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") flag.StringVar(&urlString, "u", "", "Specify the url") flag.BoolVar(&framed, "framed", false, "Use framed transport") flag.BoolVar(&useHttp, "http", false, "Use http") flag.Var(headers, "H", "Headers to set on the http(s) request (e.g. -H \"Key: Value\")") flag.Parse() if len(urlString) > 0 { var err error parsedUrl, err = url.Parse(urlString) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } host = parsedUrl.Host useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https" } else if useHttp { _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } } cmd := flag.Arg(0) var err error if useHttp { trans, err = thrift.NewTHttpClient(parsedUrl.String()) if len(headers) > 0 { httptrans := trans.(*thrift.THttpClient) for key, value := range headers { httptrans.SetHeader(key, value) } } } else { portStr := fmt.Sprint(port) if strings.Contains(host, ":") { host, portStr, err = net.SplitHostPort(host) if err != nil { fmt.Fprintln(os.Stderr, "error with host:", err) os.Exit(1) } } trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) if err != nil { fmt.Fprintln(os.Stderr, "error resolving address:", err) os.Exit(1) } if framed { trans = thrift.NewTFramedTransport(trans) } } if err != nil { fmt.Fprintln(os.Stderr, "Error creating transport", err) os.Exit(1) } defer trans.Close() var protocolFactory thrift.TProtocolFactory switch protocol { case "compact": protocolFactory = thrift.NewTCompactProtocolFactory() break case "simplejson": protocolFactory = thrift.NewTSimpleJSONProtocolFactory() break case "json": protocolFactory = thrift.NewTJSONProtocolFactory() break case "binary", "": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() break default: fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) Usage() os.Exit(1) } iprot := protocolFactory.GetProtocol(trans) oprot := protocolFactory.GetProtocol(trans) client := agent.NewAgentClient(thrift.NewTStandardClient(iprot, oprot)) if err := trans.Open(); err != nil { fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) os.Exit(1) } switch cmd { case "emitZipkinBatch": if flag.NArg()-1 != 1 { fmt.Fprintln(os.Stderr, "EmitZipkinBatch requires 1 args") flag.Usage() } arg5 := flag.Arg(1) mbTrans6 := thrift.NewTMemoryBufferLen(len(arg5)) defer mbTrans6.Close() _, err7 := mbTrans6.WriteString(arg5) if err7 != nil { Usage() return } factory8 := thrift.NewTJSONProtocolFactory() jsProt9 := factory8.GetProtocol(mbTrans6) containerStruct0 := agent.NewAgentEmitZipkinBatchArgs() err10 := containerStruct0.ReadField1(context.Background(), jsProt9) if err10 != nil { Usage() return } argvalue0 := containerStruct0.Spans value0 := argvalue0 fmt.Print(client.EmitZipkinBatch(context.Background(), value0)) fmt.Print("\n") break case "emitBatch": if flag.NArg()-1 != 1 { fmt.Fprintln(os.Stderr, "EmitBatch requires 1 args") flag.Usage() } arg11 := flag.Arg(1) mbTrans12 := thrift.NewTMemoryBufferLen(len(arg11)) defer mbTrans12.Close() _, err13 := mbTrans12.WriteString(arg11) if err13 != nil { Usage() return } factory14 := thrift.NewTJSONProtocolFactory() jsProt15 := factory14.GetProtocol(mbTrans12) argvalue0 := jaeger.NewBatch() err16 := argvalue0.Read(context.Background(), jsProt15) if err16 != nil { Usage() return } value0 := argvalue0 fmt.Print(client.EmitBatch(context.Background(), value0)) fmt.Print("\n") break case "": Usage() break default: fmt.Fprintln(os.Stderr, "Invalid function ", cmd) } } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/agent/agent.go000066400000000000000000000265721414226744000276360ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package agent import ( "bytes" "context" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal var _ = jaeger.GoUnusedProtection__ var _ = zipkincore.GoUnusedProtection__ type Agent interface { // Parameters: // - Spans EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error) // Parameters: // - Batch EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error) } type AgentClient struct { c thrift.TClient meta thrift.ResponseMeta } func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { return &AgentClient{ c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), } } func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { return &AgentClient{ c: thrift.NewTStandardClient(iprot, oprot), } } func NewAgentClient(c thrift.TClient) *AgentClient { return &AgentClient{ c: c, } } func (p *AgentClient) Client_() thrift.TClient { return p.c } func (p *AgentClient) LastResponseMeta_() thrift.ResponseMeta { return p.meta } func (p *AgentClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { p.meta = meta } // Parameters: // - Spans func (p *AgentClient) EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error) { var _args0 AgentEmitZipkinBatchArgs _args0.Spans = spans p.SetLastResponseMeta_(thrift.ResponseMeta{}) if _, err := p.Client_().Call(ctx, "emitZipkinBatch", &_args0, nil); err != nil { return err } return nil } // Parameters: // - Batch func (p *AgentClient) EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error) { var _args1 AgentEmitBatchArgs _args1.Batch = batch p.SetLastResponseMeta_(thrift.ResponseMeta{}) if _, err := p.Client_().Call(ctx, "emitBatch", &_args1, nil); err != nil { return err } return nil } type AgentProcessor struct { processorMap map[string]thrift.TProcessorFunction handler Agent } func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { p.processorMap[key] = processor } func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { processor, ok = p.processorMap[key] return processor, ok } func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { return p.processorMap } func NewAgentProcessor(handler Agent) *AgentProcessor { self2 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self2.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler} self2.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} return self2 } func (p *AgentProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) if err2 != nil { return false, thrift.WrapTException(err2) } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(ctx, seqId, iprot, oprot) } iprot.Skip(ctx, thrift.STRUCT) iprot.ReadMessageEnd(ctx) x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) x3.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return false, x3 } type agentProcessorEmitZipkinBatch struct { handler Agent } func (p *agentProcessorEmitZipkinBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := AgentEmitZipkinBatchArgs{} var err2 error if err2 = args.Read(ctx, iprot); err2 != nil { iprot.ReadMessageEnd(ctx) return false, thrift.WrapTException(err2) } iprot.ReadMessageEnd(ctx) tickerCancel := func() {} _ = tickerCancel if err2 = p.handler.EmitZipkinBatch(ctx, args.Spans); err2 != nil { tickerCancel() return true, thrift.WrapTException(err2) } tickerCancel() return true, nil } type agentProcessorEmitBatch struct { handler Agent } func (p *agentProcessorEmitBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := AgentEmitBatchArgs{} var err2 error if err2 = args.Read(ctx, iprot); err2 != nil { iprot.ReadMessageEnd(ctx) return false, thrift.WrapTException(err2) } iprot.ReadMessageEnd(ctx) tickerCancel := func() {} _ = tickerCancel if err2 = p.handler.EmitBatch(ctx, args.Batch); err2 != nil { tickerCancel() return true, thrift.WrapTException(err2) } tickerCancel() return true, nil } // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Spans type AgentEmitZipkinBatchArgs struct { Spans []*zipkincore.Span `thrift:"spans,1" db:"spans" json:"spans"` } func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs { return &AgentEmitZipkinBatchArgs{} } func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span { return p.Spans } func (p *AgentEmitZipkinBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.LIST { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *AgentEmitZipkinBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*zipkincore.Span, 0, size) p.Spans = tSlice for i := 0; i < size; i++ { _elem4 := &zipkincore.Span{} if err := _elem4.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) } p.Spans = append(p.Spans, _elem4) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *AgentEmitZipkinBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "emitZipkinBatch_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *AgentEmitZipkinBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Spans { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) } return err } func (p *AgentEmitZipkinBatchArgs) String() string { if p == nil { return "" } return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) } // Attributes: // - Batch type AgentEmitBatchArgs struct { Batch *jaeger.Batch `thrift:"batch,1" db:"batch" json:"batch"` } func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { return &AgentEmitBatchArgs{} } var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch { if !p.IsSetBatch() { return AgentEmitBatchArgs_Batch_DEFAULT } return p.Batch } func (p *AgentEmitBatchArgs) IsSetBatch() bool { return p.Batch != nil } func (p *AgentEmitBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.STRUCT { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *AgentEmitBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { p.Batch = &jaeger.Batch{} if err := p.Batch.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) } return nil } func (p *AgentEmitBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "emitBatch_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *AgentEmitBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "batch", thrift.STRUCT, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) } if err := p.Batch.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) } return err } func (p *AgentEmitBatchArgs) String() string { if p == nil { return "" } return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/000077500000000000000000000000001414226744000263345ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/GoUnusedProtection__.go000066400000000000000000000001541414226744000327610ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package jaeger var GoUnusedProtection__ int; golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/collector-remote/000077500000000000000000000000001414226744000316135ustar00rootroot00000000000000collector-remote.go000077500000000000000000000111561414226744000353510ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/collector-remote// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package main import ( "context" "flag" "fmt" "math" "net" "net/url" "os" "strconv" "strings" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) var _ = jaeger.GoUnusedProtection__ func Usage() { fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\nFunctions:") fmt.Fprintln(os.Stderr, " submitBatches( batches)") fmt.Fprintln(os.Stderr) os.Exit(0) } type httpHeaders map[string]string func (h httpHeaders) String() string { var m map[string]string = h return fmt.Sprintf("%s", m) } func (h httpHeaders) Set(value string) error { parts := strings.Split(value, ": ") if len(parts) != 2 { return fmt.Errorf("header should be of format 'Key: Value'") } h[parts[0]] = parts[1] return nil } func main() { flag.Usage = Usage var host string var port int var protocol string var urlString string var framed bool var useHttp bool headers := make(httpHeaders) var parsedUrl *url.URL var trans thrift.TTransport _ = strconv.Atoi _ = math.Abs flag.Usage = Usage flag.StringVar(&host, "h", "localhost", "Specify host and port") flag.IntVar(&port, "p", 9090, "Specify port") flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") flag.StringVar(&urlString, "u", "", "Specify the url") flag.BoolVar(&framed, "framed", false, "Use framed transport") flag.BoolVar(&useHttp, "http", false, "Use http") flag.Var(headers, "H", "Headers to set on the http(s) request (e.g. -H \"Key: Value\")") flag.Parse() if len(urlString) > 0 { var err error parsedUrl, err = url.Parse(urlString) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } host = parsedUrl.Host useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https" } else if useHttp { _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } } cmd := flag.Arg(0) var err error if useHttp { trans, err = thrift.NewTHttpClient(parsedUrl.String()) if len(headers) > 0 { httptrans := trans.(*thrift.THttpClient) for key, value := range headers { httptrans.SetHeader(key, value) } } } else { portStr := fmt.Sprint(port) if strings.Contains(host, ":") { host, portStr, err = net.SplitHostPort(host) if err != nil { fmt.Fprintln(os.Stderr, "error with host:", err) os.Exit(1) } } trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) if err != nil { fmt.Fprintln(os.Stderr, "error resolving address:", err) os.Exit(1) } if framed { trans = thrift.NewTFramedTransport(trans) } } if err != nil { fmt.Fprintln(os.Stderr, "Error creating transport", err) os.Exit(1) } defer trans.Close() var protocolFactory thrift.TProtocolFactory switch protocol { case "compact": protocolFactory = thrift.NewTCompactProtocolFactory() break case "simplejson": protocolFactory = thrift.NewTSimpleJSONProtocolFactory() break case "json": protocolFactory = thrift.NewTJSONProtocolFactory() break case "binary", "": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() break default: fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) Usage() os.Exit(1) } iprot := protocolFactory.GetProtocol(trans) oprot := protocolFactory.GetProtocol(trans) client := jaeger.NewCollectorClient(thrift.NewTStandardClient(iprot, oprot)) if err := trans.Open(); err != nil { fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) os.Exit(1) } switch cmd { case "submitBatches": if flag.NArg()-1 != 1 { fmt.Fprintln(os.Stderr, "SubmitBatches requires 1 args") flag.Usage() } arg19 := flag.Arg(1) mbTrans20 := thrift.NewTMemoryBufferLen(len(arg19)) defer mbTrans20.Close() _, err21 := mbTrans20.WriteString(arg19) if err21 != nil { Usage() return } factory22 := thrift.NewTJSONProtocolFactory() jsProt23 := factory22.GetProtocol(mbTrans20) containerStruct0 := jaeger.NewCollectorSubmitBatchesArgs() err24 := containerStruct0.ReadField1(context.Background(), jsProt23) if err24 != nil { Usage() return } argvalue0 := containerStruct0.Batches value0 := argvalue0 fmt.Print(client.SubmitBatches(context.Background(), value0)) fmt.Print("\n") break case "": Usage() break default: fmt.Fprintln(os.Stderr, "Invalid function ", cmd) } } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/jaeger-consts.go000066400000000000000000000006241414226744000314310ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package jaeger import ( "bytes" "context" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal func init() { } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/jaeger/jaeger.go000066400000000000000000002463701414226744000301340ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package jaeger import ( "bytes" "context" "database/sql/driver" "errors" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal type TagType int64 const ( TagType_STRING TagType = 0 TagType_DOUBLE TagType = 1 TagType_BOOL TagType = 2 TagType_LONG TagType = 3 TagType_BINARY TagType = 4 ) func (p TagType) String() string { switch p { case TagType_STRING: return "STRING" case TagType_DOUBLE: return "DOUBLE" case TagType_BOOL: return "BOOL" case TagType_LONG: return "LONG" case TagType_BINARY: return "BINARY" } return "" } func TagTypeFromString(s string) (TagType, error) { switch s { case "STRING": return TagType_STRING, nil case "DOUBLE": return TagType_DOUBLE, nil case "BOOL": return TagType_BOOL, nil case "LONG": return TagType_LONG, nil case "BINARY": return TagType_BINARY, nil } return TagType(0), fmt.Errorf("not a valid TagType string") } func TagTypePtr(v TagType) *TagType { return &v } func (p TagType) MarshalText() ([]byte, error) { return []byte(p.String()), nil } func (p *TagType) UnmarshalText(text []byte) error { q, err := TagTypeFromString(string(text)) if err != nil { return err } *p = q return nil } func (p *TagType) Scan(value interface{}) error { v, ok := value.(int64) if !ok { return errors.New("Scan value is not int64") } *p = TagType(v) return nil } func (p *TagType) Value() (driver.Value, error) { if p == nil { return nil, nil } return int64(*p), nil } type SpanRefType int64 const ( SpanRefType_CHILD_OF SpanRefType = 0 SpanRefType_FOLLOWS_FROM SpanRefType = 1 ) func (p SpanRefType) String() string { switch p { case SpanRefType_CHILD_OF: return "CHILD_OF" case SpanRefType_FOLLOWS_FROM: return "FOLLOWS_FROM" } return "" } func SpanRefTypeFromString(s string) (SpanRefType, error) { switch s { case "CHILD_OF": return SpanRefType_CHILD_OF, nil case "FOLLOWS_FROM": return SpanRefType_FOLLOWS_FROM, nil } return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") } func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } func (p SpanRefType) MarshalText() ([]byte, error) { return []byte(p.String()), nil } func (p *SpanRefType) UnmarshalText(text []byte) error { q, err := SpanRefTypeFromString(string(text)) if err != nil { return err } *p = q return nil } func (p *SpanRefType) Scan(value interface{}) error { v, ok := value.(int64) if !ok { return errors.New("Scan value is not int64") } *p = SpanRefType(v) return nil } func (p *SpanRefType) Value() (driver.Value, error) { if p == nil { return nil, nil } return int64(*p), nil } // Attributes: // - Key // - VType // - VStr // - VDouble // - VBool // - VLong // - VBinary type Tag struct { Key string `thrift:"key,1,required" db:"key" json:"key"` VType TagType `thrift:"vType,2,required" db:"vType" json:"vType"` VStr *string `thrift:"vStr,3" db:"vStr" json:"vStr,omitempty"` VDouble *float64 `thrift:"vDouble,4" db:"vDouble" json:"vDouble,omitempty"` VBool *bool `thrift:"vBool,5" db:"vBool" json:"vBool,omitempty"` VLong *int64 `thrift:"vLong,6" db:"vLong" json:"vLong,omitempty"` VBinary []byte `thrift:"vBinary,7" db:"vBinary" json:"vBinary,omitempty"` } func NewTag() *Tag { return &Tag{} } func (p *Tag) GetKey() string { return p.Key } func (p *Tag) GetVType() TagType { return p.VType } var Tag_VStr_DEFAULT string func (p *Tag) GetVStr() string { if !p.IsSetVStr() { return Tag_VStr_DEFAULT } return *p.VStr } var Tag_VDouble_DEFAULT float64 func (p *Tag) GetVDouble() float64 { if !p.IsSetVDouble() { return Tag_VDouble_DEFAULT } return *p.VDouble } var Tag_VBool_DEFAULT bool func (p *Tag) GetVBool() bool { if !p.IsSetVBool() { return Tag_VBool_DEFAULT } return *p.VBool } var Tag_VLong_DEFAULT int64 func (p *Tag) GetVLong() int64 { if !p.IsSetVLong() { return Tag_VLong_DEFAULT } return *p.VLong } var Tag_VBinary_DEFAULT []byte func (p *Tag) GetVBinary() []byte { return p.VBinary } func (p *Tag) IsSetVStr() bool { return p.VStr != nil } func (p *Tag) IsSetVDouble() bool { return p.VDouble != nil } func (p *Tag) IsSetVBool() bool { return p.VBool != nil } func (p *Tag) IsSetVLong() bool { return p.VLong != nil } func (p *Tag) IsSetVBinary() bool { return p.VBinary != nil } func (p *Tag) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetKey bool = false var issetVType bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.STRING { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetKey = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.I32 { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetVType = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.STRING { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.DOUBLE { if err := p.ReadField4(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 5: if fieldTypeId == thrift.BOOL { if err := p.ReadField5(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 6: if fieldTypeId == thrift.I64 { if err := p.ReadField6(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 7: if fieldTypeId == thrift.STRING { if err := p.ReadField7(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetKey { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) } if !issetVType { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")) } return nil } func (p *Tag) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Key = v } return nil } func (p *Tag) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { temp := TagType(v) p.VType = temp } return nil } func (p *Tag) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.VStr = &v } return nil } func (p *Tag) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadDouble(ctx); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.VDouble = &v } return nil } func (p *Tag) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBool(ctx); err != nil { return thrift.PrependError("error reading field 5: ", err) } else { p.VBool = &v } return nil } func (p *Tag) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 6: ", err) } else { p.VLong = &v } return nil } func (p *Tag) ReadField7(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBinary(ctx); err != nil { return thrift.PrependError("error reading field 7: ", err) } else { p.VBinary = v } return nil } func (p *Tag) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Tag"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } if err := p.writeField5(ctx, oprot); err != nil { return err } if err := p.writeField6(ctx, oprot); err != nil { return err } if err := p.writeField7(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Tag) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "key", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) } if err := oprot.WriteString(ctx, string(p.Key)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) } return err } func (p *Tag) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "vType", thrift.I32, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) } if err := oprot.WriteI32(ctx, int32(p.VType)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) } return err } func (p *Tag) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetVStr() { if err := oprot.WriteFieldBegin(ctx, "vStr", thrift.STRING, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) } if err := oprot.WriteString(ctx, string(*p.VStr)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) } } return err } func (p *Tag) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetVDouble() { if err := oprot.WriteFieldBegin(ctx, "vDouble", thrift.DOUBLE, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) } if err := oprot.WriteDouble(ctx, float64(*p.VDouble)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) } } return err } func (p *Tag) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetVBool() { if err := oprot.WriteFieldBegin(ctx, "vBool", thrift.BOOL, 5); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) } if err := oprot.WriteBool(ctx, bool(*p.VBool)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) } } return err } func (p *Tag) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetVLong() { if err := oprot.WriteFieldBegin(ctx, "vLong", thrift.I64, 6); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.VLong)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) } } return err } func (p *Tag) writeField7(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetVBinary() { if err := oprot.WriteFieldBegin(ctx, "vBinary", thrift.STRING, 7); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) } if err := oprot.WriteBinary(ctx, p.VBinary); err != nil { return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) } } return err } func (p *Tag) Equals(other *Tag) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Key != other.Key { return false } if p.VType != other.VType { return false } if p.VStr != other.VStr { if p.VStr == nil || other.VStr == nil { return false } if (*p.VStr) != (*other.VStr) { return false } } if p.VDouble != other.VDouble { if p.VDouble == nil || other.VDouble == nil { return false } if (*p.VDouble) != (*other.VDouble) { return false } } if p.VBool != other.VBool { if p.VBool == nil || other.VBool == nil { return false } if (*p.VBool) != (*other.VBool) { return false } } if p.VLong != other.VLong { if p.VLong == nil || other.VLong == nil { return false } if (*p.VLong) != (*other.VLong) { return false } } if bytes.Compare(p.VBinary, other.VBinary) != 0 { return false } return true } func (p *Tag) String() string { if p == nil { return "" } return fmt.Sprintf("Tag(%+v)", *p) } // Attributes: // - Timestamp // - Fields type Log struct { Timestamp int64 `thrift:"timestamp,1,required" db:"timestamp" json:"timestamp"` Fields []*Tag `thrift:"fields,2,required" db:"fields" json:"fields"` } func NewLog() *Log { return &Log{} } func (p *Log) GetTimestamp() int64 { return p.Timestamp } func (p *Log) GetFields() []*Tag { return p.Fields } func (p *Log) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetTimestamp bool = false var issetFields bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I64 { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetTimestamp = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.LIST { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetFields = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetTimestamp { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) } if !issetFields { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")) } return nil } func (p *Log) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Timestamp = v } return nil } func (p *Log) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Tag, 0, size) p.Fields = tSlice for i := 0; i < size; i++ { _elem0 := &Tag{} if err := _elem0.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) } p.Fields = append(p.Fields, _elem0) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Log) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Log"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Log) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.Timestamp)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) } return err } func (p *Log) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "fields", thrift.LIST, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Fields)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Fields { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) } return err } func (p *Log) Equals(other *Log) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Timestamp != other.Timestamp { return false } if len(p.Fields) != len(other.Fields) { return false } for i, _tgt := range p.Fields { _src1 := other.Fields[i] if !_tgt.Equals(_src1) { return false } } return true } func (p *Log) String() string { if p == nil { return "" } return fmt.Sprintf("Log(%+v)", *p) } // Attributes: // - RefType // - TraceIdLow // - TraceIdHigh // - SpanId type SpanRef struct { RefType SpanRefType `thrift:"refType,1,required" db:"refType" json:"refType"` TraceIdLow int64 `thrift:"traceIdLow,2,required" db:"traceIdLow" json:"traceIdLow"` TraceIdHigh int64 `thrift:"traceIdHigh,3,required" db:"traceIdHigh" json:"traceIdHigh"` SpanId int64 `thrift:"spanId,4,required" db:"spanId" json:"spanId"` } func NewSpanRef() *SpanRef { return &SpanRef{} } func (p *SpanRef) GetRefType() SpanRefType { return p.RefType } func (p *SpanRef) GetTraceIdLow() int64 { return p.TraceIdLow } func (p *SpanRef) GetTraceIdHigh() int64 { return p.TraceIdHigh } func (p *SpanRef) GetSpanId() int64 { return p.SpanId } func (p *SpanRef) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetRefType bool = false var issetTraceIdLow bool = false var issetTraceIdHigh bool = false var issetSpanId bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I32 { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetRefType = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.I64 { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetTraceIdLow = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.I64 { if err := p.ReadField3(ctx, iprot); err != nil { return err } issetTraceIdHigh = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.I64 { if err := p.ReadField4(ctx, iprot); err != nil { return err } issetSpanId = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetRefType { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")) } if !issetTraceIdLow { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) } if !issetTraceIdHigh { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) } if !issetSpanId { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) } return nil } func (p *SpanRef) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { temp := SpanRefType(v) p.RefType = temp } return nil } func (p *SpanRef) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.TraceIdLow = v } return nil } func (p *SpanRef) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.TraceIdHigh = v } return nil } func (p *SpanRef) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.SpanId = v } return nil } func (p *SpanRef) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "SpanRef"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *SpanRef) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "refType", thrift.I32, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) } if err := oprot.WriteI32(ctx, int32(p.RefType)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) } return err } func (p *SpanRef) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "traceIdLow", thrift.I64, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TraceIdLow)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) } return err } func (p *SpanRef) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "traceIdHigh", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TraceIdHigh)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) } return err } func (p *SpanRef) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "spanId", thrift.I64, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.SpanId)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) } return err } func (p *SpanRef) Equals(other *SpanRef) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.RefType != other.RefType { return false } if p.TraceIdLow != other.TraceIdLow { return false } if p.TraceIdHigh != other.TraceIdHigh { return false } if p.SpanId != other.SpanId { return false } return true } func (p *SpanRef) String() string { if p == nil { return "" } return fmt.Sprintf("SpanRef(%+v)", *p) } // Attributes: // - TraceIdLow // - TraceIdHigh // - SpanId // - ParentSpanId // - OperationName // - References // - Flags // - StartTime // - Duration // - Tags // - Logs type Span struct { TraceIdLow int64 `thrift:"traceIdLow,1,required" db:"traceIdLow" json:"traceIdLow"` TraceIdHigh int64 `thrift:"traceIdHigh,2,required" db:"traceIdHigh" json:"traceIdHigh"` SpanId int64 `thrift:"spanId,3,required" db:"spanId" json:"spanId"` ParentSpanId int64 `thrift:"parentSpanId,4,required" db:"parentSpanId" json:"parentSpanId"` OperationName string `thrift:"operationName,5,required" db:"operationName" json:"operationName"` References []*SpanRef `thrift:"references,6" db:"references" json:"references,omitempty"` Flags int32 `thrift:"flags,7,required" db:"flags" json:"flags"` StartTime int64 `thrift:"startTime,8,required" db:"startTime" json:"startTime"` Duration int64 `thrift:"duration,9,required" db:"duration" json:"duration"` Tags []*Tag `thrift:"tags,10" db:"tags" json:"tags,omitempty"` Logs []*Log `thrift:"logs,11" db:"logs" json:"logs,omitempty"` } func NewSpan() *Span { return &Span{} } func (p *Span) GetTraceIdLow() int64 { return p.TraceIdLow } func (p *Span) GetTraceIdHigh() int64 { return p.TraceIdHigh } func (p *Span) GetSpanId() int64 { return p.SpanId } func (p *Span) GetParentSpanId() int64 { return p.ParentSpanId } func (p *Span) GetOperationName() string { return p.OperationName } var Span_References_DEFAULT []*SpanRef func (p *Span) GetReferences() []*SpanRef { return p.References } func (p *Span) GetFlags() int32 { return p.Flags } func (p *Span) GetStartTime() int64 { return p.StartTime } func (p *Span) GetDuration() int64 { return p.Duration } var Span_Tags_DEFAULT []*Tag func (p *Span) GetTags() []*Tag { return p.Tags } var Span_Logs_DEFAULT []*Log func (p *Span) GetLogs() []*Log { return p.Logs } func (p *Span) IsSetReferences() bool { return p.References != nil } func (p *Span) IsSetTags() bool { return p.Tags != nil } func (p *Span) IsSetLogs() bool { return p.Logs != nil } func (p *Span) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetTraceIdLow bool = false var issetTraceIdHigh bool = false var issetSpanId bool = false var issetParentSpanId bool = false var issetOperationName bool = false var issetFlags bool = false var issetStartTime bool = false var issetDuration bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I64 { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetTraceIdLow = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.I64 { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetTraceIdHigh = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.I64 { if err := p.ReadField3(ctx, iprot); err != nil { return err } issetSpanId = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.I64 { if err := p.ReadField4(ctx, iprot); err != nil { return err } issetParentSpanId = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 5: if fieldTypeId == thrift.STRING { if err := p.ReadField5(ctx, iprot); err != nil { return err } issetOperationName = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 6: if fieldTypeId == thrift.LIST { if err := p.ReadField6(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 7: if fieldTypeId == thrift.I32 { if err := p.ReadField7(ctx, iprot); err != nil { return err } issetFlags = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 8: if fieldTypeId == thrift.I64 { if err := p.ReadField8(ctx, iprot); err != nil { return err } issetStartTime = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 9: if fieldTypeId == thrift.I64 { if err := p.ReadField9(ctx, iprot); err != nil { return err } issetDuration = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 10: if fieldTypeId == thrift.LIST { if err := p.ReadField10(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 11: if fieldTypeId == thrift.LIST { if err := p.ReadField11(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetTraceIdLow { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) } if !issetTraceIdHigh { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) } if !issetSpanId { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) } if !issetParentSpanId { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")) } if !issetOperationName { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")) } if !issetFlags { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")) } if !issetStartTime { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")) } if !issetDuration { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")) } return nil } func (p *Span) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.TraceIdLow = v } return nil } func (p *Span) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.TraceIdHigh = v } return nil } func (p *Span) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.SpanId = v } return nil } func (p *Span) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.ParentSpanId = v } return nil } func (p *Span) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 5: ", err) } else { p.OperationName = v } return nil } func (p *Span) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*SpanRef, 0, size) p.References = tSlice for i := 0; i < size; i++ { _elem2 := &SpanRef{} if err := _elem2.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) } p.References = append(p.References, _elem2) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Span) ReadField7(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(ctx); err != nil { return thrift.PrependError("error reading field 7: ", err) } else { p.Flags = v } return nil } func (p *Span) ReadField8(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 8: ", err) } else { p.StartTime = v } return nil } func (p *Span) ReadField9(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 9: ", err) } else { p.Duration = v } return nil } func (p *Span) ReadField10(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Tag, 0, size) p.Tags = tSlice for i := 0; i < size; i++ { _elem3 := &Tag{} if err := _elem3.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) } p.Tags = append(p.Tags, _elem3) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Span) ReadField11(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Log, 0, size) p.Logs = tSlice for i := 0; i < size; i++ { _elem4 := &Log{} if err := _elem4.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) } p.Logs = append(p.Logs, _elem4) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Span) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Span"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } if err := p.writeField5(ctx, oprot); err != nil { return err } if err := p.writeField6(ctx, oprot); err != nil { return err } if err := p.writeField7(ctx, oprot); err != nil { return err } if err := p.writeField8(ctx, oprot); err != nil { return err } if err := p.writeField9(ctx, oprot); err != nil { return err } if err := p.writeField10(ctx, oprot); err != nil { return err } if err := p.writeField11(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Span) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "traceIdLow", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TraceIdLow)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) } return err } func (p *Span) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "traceIdHigh", thrift.I64, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TraceIdHigh)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) } return err } func (p *Span) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "spanId", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.SpanId)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) } return err } func (p *Span) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "parentSpanId", thrift.I64, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.ParentSpanId)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) } return err } func (p *Span) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "operationName", thrift.STRING, 5); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) } if err := oprot.WriteString(ctx, string(p.OperationName)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) } return err } func (p *Span) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetReferences() { if err := oprot.WriteFieldBegin(ctx, "references", thrift.LIST, 6); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.References)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.References { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) } } return err } func (p *Span) writeField7(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "flags", thrift.I32, 7); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) } if err := oprot.WriteI32(ctx, int32(p.Flags)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) } return err } func (p *Span) writeField8(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "startTime", thrift.I64, 8); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.StartTime)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) } return err } func (p *Span) writeField9(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "duration", thrift.I64, 9); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.Duration)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) } return err } func (p *Span) writeField10(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetTags() { if err := oprot.WriteFieldBegin(ctx, "tags", thrift.LIST, 10); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Tags)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Tags { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) } } return err } func (p *Span) writeField11(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetLogs() { if err := oprot.WriteFieldBegin(ctx, "logs", thrift.LIST, 11); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Logs)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Logs { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) } } return err } func (p *Span) Equals(other *Span) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.TraceIdLow != other.TraceIdLow { return false } if p.TraceIdHigh != other.TraceIdHigh { return false } if p.SpanId != other.SpanId { return false } if p.ParentSpanId != other.ParentSpanId { return false } if p.OperationName != other.OperationName { return false } if len(p.References) != len(other.References) { return false } for i, _tgt := range p.References { _src5 := other.References[i] if !_tgt.Equals(_src5) { return false } } if p.Flags != other.Flags { return false } if p.StartTime != other.StartTime { return false } if p.Duration != other.Duration { return false } if len(p.Tags) != len(other.Tags) { return false } for i, _tgt := range p.Tags { _src6 := other.Tags[i] if !_tgt.Equals(_src6) { return false } } if len(p.Logs) != len(other.Logs) { return false } for i, _tgt := range p.Logs { _src7 := other.Logs[i] if !_tgt.Equals(_src7) { return false } } return true } func (p *Span) String() string { if p == nil { return "" } return fmt.Sprintf("Span(%+v)", *p) } // Attributes: // - ServiceName // - Tags type Process struct { ServiceName string `thrift:"serviceName,1,required" db:"serviceName" json:"serviceName"` Tags []*Tag `thrift:"tags,2" db:"tags" json:"tags,omitempty"` } func NewProcess() *Process { return &Process{} } func (p *Process) GetServiceName() string { return p.ServiceName } var Process_Tags_DEFAULT []*Tag func (p *Process) GetTags() []*Tag { return p.Tags } func (p *Process) IsSetTags() bool { return p.Tags != nil } func (p *Process) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetServiceName bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.STRING { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetServiceName = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.LIST { if err := p.ReadField2(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetServiceName { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) } return nil } func (p *Process) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.ServiceName = v } return nil } func (p *Process) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Tag, 0, size) p.Tags = tSlice for i := 0; i < size; i++ { _elem8 := &Tag{} if err := _elem8.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem8), err) } p.Tags = append(p.Tags, _elem8) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Process) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Process"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Process) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "serviceName", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) } if err := oprot.WriteString(ctx, string(p.ServiceName)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) } return err } func (p *Process) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetTags() { if err := oprot.WriteFieldBegin(ctx, "tags", thrift.LIST, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Tags)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Tags { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) } } return err } func (p *Process) Equals(other *Process) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.ServiceName != other.ServiceName { return false } if len(p.Tags) != len(other.Tags) { return false } for i, _tgt := range p.Tags { _src9 := other.Tags[i] if !_tgt.Equals(_src9) { return false } } return true } func (p *Process) String() string { if p == nil { return "" } return fmt.Sprintf("Process(%+v)", *p) } // Attributes: // - FullQueueDroppedSpans // - TooLargeDroppedSpans // - FailedToEmitSpans type ClientStats struct { FullQueueDroppedSpans int64 `thrift:"fullQueueDroppedSpans,1,required" db:"fullQueueDroppedSpans" json:"fullQueueDroppedSpans"` TooLargeDroppedSpans int64 `thrift:"tooLargeDroppedSpans,2,required" db:"tooLargeDroppedSpans" json:"tooLargeDroppedSpans"` FailedToEmitSpans int64 `thrift:"failedToEmitSpans,3,required" db:"failedToEmitSpans" json:"failedToEmitSpans"` } func NewClientStats() *ClientStats { return &ClientStats{} } func (p *ClientStats) GetFullQueueDroppedSpans() int64 { return p.FullQueueDroppedSpans } func (p *ClientStats) GetTooLargeDroppedSpans() int64 { return p.TooLargeDroppedSpans } func (p *ClientStats) GetFailedToEmitSpans() int64 { return p.FailedToEmitSpans } func (p *ClientStats) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetFullQueueDroppedSpans bool = false var issetTooLargeDroppedSpans bool = false var issetFailedToEmitSpans bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I64 { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetFullQueueDroppedSpans = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.I64 { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetTooLargeDroppedSpans = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.I64 { if err := p.ReadField3(ctx, iprot); err != nil { return err } issetFailedToEmitSpans = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetFullQueueDroppedSpans { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FullQueueDroppedSpans is not set")) } if !issetTooLargeDroppedSpans { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TooLargeDroppedSpans is not set")) } if !issetFailedToEmitSpans { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FailedToEmitSpans is not set")) } return nil } func (p *ClientStats) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.FullQueueDroppedSpans = v } return nil } func (p *ClientStats) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.TooLargeDroppedSpans = v } return nil } func (p *ClientStats) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.FailedToEmitSpans = v } return nil } func (p *ClientStats) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "ClientStats"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *ClientStats) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "fullQueueDroppedSpans", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:fullQueueDroppedSpans: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.FullQueueDroppedSpans)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.fullQueueDroppedSpans (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:fullQueueDroppedSpans: ", p), err) } return err } func (p *ClientStats) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "tooLargeDroppedSpans", thrift.I64, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tooLargeDroppedSpans: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TooLargeDroppedSpans)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.tooLargeDroppedSpans (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tooLargeDroppedSpans: ", p), err) } return err } func (p *ClientStats) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "failedToEmitSpans", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:failedToEmitSpans: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.FailedToEmitSpans)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.failedToEmitSpans (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:failedToEmitSpans: ", p), err) } return err } func (p *ClientStats) Equals(other *ClientStats) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.FullQueueDroppedSpans != other.FullQueueDroppedSpans { return false } if p.TooLargeDroppedSpans != other.TooLargeDroppedSpans { return false } if p.FailedToEmitSpans != other.FailedToEmitSpans { return false } return true } func (p *ClientStats) String() string { if p == nil { return "" } return fmt.Sprintf("ClientStats(%+v)", *p) } // Attributes: // - Process // - Spans // - SeqNo // - Stats type Batch struct { Process *Process `thrift:"process,1,required" db:"process" json:"process"` Spans []*Span `thrift:"spans,2,required" db:"spans" json:"spans"` SeqNo *int64 `thrift:"seqNo,3" db:"seqNo" json:"seqNo,omitempty"` Stats *ClientStats `thrift:"stats,4" db:"stats" json:"stats,omitempty"` } func NewBatch() *Batch { return &Batch{} } var Batch_Process_DEFAULT *Process func (p *Batch) GetProcess() *Process { if !p.IsSetProcess() { return Batch_Process_DEFAULT } return p.Process } func (p *Batch) GetSpans() []*Span { return p.Spans } var Batch_SeqNo_DEFAULT int64 func (p *Batch) GetSeqNo() int64 { if !p.IsSetSeqNo() { return Batch_SeqNo_DEFAULT } return *p.SeqNo } var Batch_Stats_DEFAULT *ClientStats func (p *Batch) GetStats() *ClientStats { if !p.IsSetStats() { return Batch_Stats_DEFAULT } return p.Stats } func (p *Batch) IsSetProcess() bool { return p.Process != nil } func (p *Batch) IsSetSeqNo() bool { return p.SeqNo != nil } func (p *Batch) IsSetStats() bool { return p.Stats != nil } func (p *Batch) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetProcess bool = false var issetSpans bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.STRUCT { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetProcess = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.LIST { if err := p.ReadField2(ctx, iprot); err != nil { return err } issetSpans = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.I64 { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.STRUCT { if err := p.ReadField4(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetProcess { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")) } if !issetSpans { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")) } return nil } func (p *Batch) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { p.Process = &Process{} if err := p.Process.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) } return nil } func (p *Batch) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Span, 0, size) p.Spans = tSlice for i := 0; i < size; i++ { _elem10 := &Span{} if err := _elem10.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem10), err) } p.Spans = append(p.Spans, _elem10) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Batch) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.SeqNo = &v } return nil } func (p *Batch) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { p.Stats = &ClientStats{} if err := p.Stats.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Stats), err) } return nil } func (p *Batch) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Batch"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Batch) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "process", thrift.STRUCT, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) } if err := p.Process.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) } return err } func (p *Batch) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Spans { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) } return err } func (p *Batch) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetSeqNo() { if err := oprot.WriteFieldBegin(ctx, "seqNo", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:seqNo: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.SeqNo)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.seqNo (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:seqNo: ", p), err) } } return err } func (p *Batch) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetStats() { if err := oprot.WriteFieldBegin(ctx, "stats", thrift.STRUCT, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:stats: ", p), err) } if err := p.Stats.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Stats), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:stats: ", p), err) } } return err } func (p *Batch) Equals(other *Batch) bool { if p == other { return true } else if p == nil || other == nil { return false } if !p.Process.Equals(other.Process) { return false } if len(p.Spans) != len(other.Spans) { return false } for i, _tgt := range p.Spans { _src11 := other.Spans[i] if !_tgt.Equals(_src11) { return false } } if p.SeqNo != other.SeqNo { if p.SeqNo == nil || other.SeqNo == nil { return false } if (*p.SeqNo) != (*other.SeqNo) { return false } } if !p.Stats.Equals(other.Stats) { return false } return true } func (p *Batch) String() string { if p == nil { return "" } return fmt.Sprintf("Batch(%+v)", *p) } // Attributes: // - Ok type BatchSubmitResponse struct { Ok bool `thrift:"ok,1,required" db:"ok" json:"ok"` } func NewBatchSubmitResponse() *BatchSubmitResponse { return &BatchSubmitResponse{} } func (p *BatchSubmitResponse) GetOk() bool { return p.Ok } func (p *BatchSubmitResponse) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetOk bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.BOOL { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetOk = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetOk { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) } return nil } func (p *BatchSubmitResponse) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBool(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Ok = v } return nil } func (p *BatchSubmitResponse) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "BatchSubmitResponse"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *BatchSubmitResponse) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "ok", thrift.BOOL, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) } if err := oprot.WriteBool(ctx, bool(p.Ok)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) } return err } func (p *BatchSubmitResponse) Equals(other *BatchSubmitResponse) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Ok != other.Ok { return false } return true } func (p *BatchSubmitResponse) String() string { if p == nil { return "" } return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) } type Collector interface { // Parameters: // - Batches SubmitBatches(ctx context.Context, batches []*Batch) (_r []*BatchSubmitResponse, _err error) } type CollectorClient struct { c thrift.TClient meta thrift.ResponseMeta } func NewCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *CollectorClient { return &CollectorClient{ c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), } } func NewCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *CollectorClient { return &CollectorClient{ c: thrift.NewTStandardClient(iprot, oprot), } } func NewCollectorClient(c thrift.TClient) *CollectorClient { return &CollectorClient{ c: c, } } func (p *CollectorClient) Client_() thrift.TClient { return p.c } func (p *CollectorClient) LastResponseMeta_() thrift.ResponseMeta { return p.meta } func (p *CollectorClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { p.meta = meta } // Parameters: // - Batches func (p *CollectorClient) SubmitBatches(ctx context.Context, batches []*Batch) (_r []*BatchSubmitResponse, _err error) { var _args12 CollectorSubmitBatchesArgs _args12.Batches = batches var _result14 CollectorSubmitBatchesResult var _meta13 thrift.ResponseMeta _meta13, _err = p.Client_().Call(ctx, "submitBatches", &_args12, &_result14) p.SetLastResponseMeta_(_meta13) if _err != nil { return } return _result14.GetSuccess(), nil } type CollectorProcessor struct { processorMap map[string]thrift.TProcessorFunction handler Collector } func (p *CollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { p.processorMap[key] = processor } func (p *CollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { processor, ok = p.processorMap[key] return processor, ok } func (p *CollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { return p.processorMap } func NewCollectorProcessor(handler Collector) *CollectorProcessor { self15 := &CollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self15.processorMap["submitBatches"] = &collectorProcessorSubmitBatches{handler: handler} return self15 } func (p *CollectorProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) if err2 != nil { return false, thrift.WrapTException(err2) } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(ctx, seqId, iprot, oprot) } iprot.Skip(ctx, thrift.STRUCT) iprot.ReadMessageEnd(ctx) x16 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) x16.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return false, x16 } type collectorProcessorSubmitBatches struct { handler Collector } func (p *collectorProcessorSubmitBatches) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := CollectorSubmitBatchesArgs{} var err2 error if err2 = args.Read(ctx, iprot); err2 != nil { iprot.ReadMessageEnd(ctx) x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error()) oprot.WriteMessageBegin(ctx, "submitBatches", thrift.EXCEPTION, seqId) x.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return false, thrift.WrapTException(err2) } iprot.ReadMessageEnd(ctx) tickerCancel := func() {} // Start a goroutine to do server side connectivity check. if thrift.ServerConnectivityCheckInterval > 0 { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) defer cancel() var tickerCtx context.Context tickerCtx, tickerCancel = context.WithCancel(context.Background()) defer tickerCancel() go func(ctx context.Context, cancel context.CancelFunc) { ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: if !iprot.Transport().IsOpen() { cancel() return } } } }(tickerCtx, cancel) } result := CollectorSubmitBatchesResult{} var retval []*BatchSubmitResponse if retval, err2 = p.handler.SubmitBatches(ctx, args.Batches); err2 != nil { tickerCancel() if err2 == thrift.ErrAbandonRequest { return false, thrift.WrapTException(err2) } x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitBatches: "+err2.Error()) oprot.WriteMessageBegin(ctx, "submitBatches", thrift.EXCEPTION, seqId) x.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return true, thrift.WrapTException(err2) } else { result.Success = retval } tickerCancel() if err2 = oprot.WriteMessageBegin(ctx, "submitBatches", thrift.REPLY, seqId); err2 != nil { err = thrift.WrapTException(err2) } if err2 = result.Write(ctx, oprot); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err2 = oprot.Flush(ctx); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err != nil { return } return true, err } // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Batches type CollectorSubmitBatchesArgs struct { Batches []*Batch `thrift:"batches,1" db:"batches" json:"batches"` } func NewCollectorSubmitBatchesArgs() *CollectorSubmitBatchesArgs { return &CollectorSubmitBatchesArgs{} } func (p *CollectorSubmitBatchesArgs) GetBatches() []*Batch { return p.Batches } func (p *CollectorSubmitBatchesArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.LIST { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *CollectorSubmitBatchesArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Batch, 0, size) p.Batches = tSlice for i := 0; i < size; i++ { _elem17 := &Batch{} if err := _elem17.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem17), err) } p.Batches = append(p.Batches, _elem17) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *CollectorSubmitBatchesArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "submitBatches_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *CollectorSubmitBatchesArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "batches", thrift.LIST, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batches: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Batches)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Batches { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batches: ", p), err) } return err } func (p *CollectorSubmitBatchesArgs) String() string { if p == nil { return "" } return fmt.Sprintf("CollectorSubmitBatchesArgs(%+v)", *p) } // Attributes: // - Success type CollectorSubmitBatchesResult struct { Success []*BatchSubmitResponse `thrift:"success,0" db:"success" json:"success,omitempty"` } func NewCollectorSubmitBatchesResult() *CollectorSubmitBatchesResult { return &CollectorSubmitBatchesResult{} } var CollectorSubmitBatchesResult_Success_DEFAULT []*BatchSubmitResponse func (p *CollectorSubmitBatchesResult) GetSuccess() []*BatchSubmitResponse { return p.Success } func (p *CollectorSubmitBatchesResult) IsSetSuccess() bool { return p.Success != nil } func (p *CollectorSubmitBatchesResult) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 0: if fieldTypeId == thrift.LIST { if err := p.ReadField0(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *CollectorSubmitBatchesResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*BatchSubmitResponse, 0, size) p.Success = tSlice for i := 0; i < size; i++ { _elem18 := &BatchSubmitResponse{} if err := _elem18.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem18), err) } p.Success = append(p.Success, _elem18) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *CollectorSubmitBatchesResult) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "submitBatches_result"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField0(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *CollectorSubmitBatchesResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetSuccess() { if err := oprot.WriteFieldBegin(ctx, "success", thrift.LIST, 0); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Success)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Success { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } } return err } func (p *CollectorSubmitBatchesResult) String() string { if p == nil { return "" } return fmt.Sprintf("CollectorSubmitBatchesResult(%+v)", *p) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/000077500000000000000000000000001414226744000272545ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/GoUnusedProtection__.go000066400000000000000000000001601414226744000336760ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package zipkincore var GoUnusedProtection__ int; golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/zipkin_collector-remote/000077500000000000000000000000001414226744000341175ustar00rootroot00000000000000zipkin_collector-remote.go000077500000000000000000000112321414226744000412340ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/zipkin_collector-remote// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package main import ( "context" "flag" "fmt" "math" "net" "net/url" "os" "strconv" "strings" "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) var _ = zipkincore.GoUnusedProtection__ func Usage() { fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\nFunctions:") fmt.Fprintln(os.Stderr, " submitZipkinBatch( spans)") fmt.Fprintln(os.Stderr) os.Exit(0) } type httpHeaders map[string]string func (h httpHeaders) String() string { var m map[string]string = h return fmt.Sprintf("%s", m) } func (h httpHeaders) Set(value string) error { parts := strings.Split(value, ": ") if len(parts) != 2 { return fmt.Errorf("header should be of format 'Key: Value'") } h[parts[0]] = parts[1] return nil } func main() { flag.Usage = Usage var host string var port int var protocol string var urlString string var framed bool var useHttp bool headers := make(httpHeaders) var parsedUrl *url.URL var trans thrift.TTransport _ = strconv.Atoi _ = math.Abs flag.Usage = Usage flag.StringVar(&host, "h", "localhost", "Specify host and port") flag.IntVar(&port, "p", 9090, "Specify port") flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") flag.StringVar(&urlString, "u", "", "Specify the url") flag.BoolVar(&framed, "framed", false, "Use framed transport") flag.BoolVar(&useHttp, "http", false, "Use http") flag.Var(headers, "H", "Headers to set on the http(s) request (e.g. -H \"Key: Value\")") flag.Parse() if len(urlString) > 0 { var err error parsedUrl, err = url.Parse(urlString) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } host = parsedUrl.Host useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https" } else if useHttp { _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) if err != nil { fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) flag.Usage() } } cmd := flag.Arg(0) var err error if useHttp { trans, err = thrift.NewTHttpClient(parsedUrl.String()) if len(headers) > 0 { httptrans := trans.(*thrift.THttpClient) for key, value := range headers { httptrans.SetHeader(key, value) } } } else { portStr := fmt.Sprint(port) if strings.Contains(host, ":") { host, portStr, err = net.SplitHostPort(host) if err != nil { fmt.Fprintln(os.Stderr, "error with host:", err) os.Exit(1) } } trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) if err != nil { fmt.Fprintln(os.Stderr, "error resolving address:", err) os.Exit(1) } if framed { trans = thrift.NewTFramedTransport(trans) } } if err != nil { fmt.Fprintln(os.Stderr, "Error creating transport", err) os.Exit(1) } defer trans.Close() var protocolFactory thrift.TProtocolFactory switch protocol { case "compact": protocolFactory = thrift.NewTCompactProtocolFactory() break case "simplejson": protocolFactory = thrift.NewTSimpleJSONProtocolFactory() break case "json": protocolFactory = thrift.NewTJSONProtocolFactory() break case "binary", "": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() break default: fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) Usage() os.Exit(1) } iprot := protocolFactory.GetProtocol(trans) oprot := protocolFactory.GetProtocol(trans) client := zipkincore.NewZipkinCollectorClient(thrift.NewTStandardClient(iprot, oprot)) if err := trans.Open(); err != nil { fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) os.Exit(1) } switch cmd { case "submitZipkinBatch": if flag.NArg()-1 != 1 { fmt.Fprintln(os.Stderr, "SubmitZipkinBatch requires 1 args") flag.Usage() } arg11 := flag.Arg(1) mbTrans12 := thrift.NewTMemoryBufferLen(len(arg11)) defer mbTrans12.Close() _, err13 := mbTrans12.WriteString(arg11) if err13 != nil { Usage() return } factory14 := thrift.NewTJSONProtocolFactory() jsProt15 := factory14.GetProtocol(mbTrans12) containerStruct0 := zipkincore.NewZipkinCollectorSubmitZipkinBatchArgs() err16 := containerStruct0.ReadField1(context.Background(), jsProt15) if err16 != nil { Usage() return } argvalue0 := containerStruct0.Spans value0 := argvalue0 fmt.Print(client.SubmitZipkinBatch(context.Background(), value0)) fmt.Print("\n") break case "": Usage() break default: fmt.Fprintln(os.Stderr, "Invalid function ", cmd) } } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/zipkincore-consts.go000066400000000000000000000015241414226744000332710ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package zipkincore import ( "bytes" "context" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal const CLIENT_SEND = "cs" const CLIENT_RECV = "cr" const SERVER_SEND = "ss" const SERVER_RECV = "sr" const MESSAGE_SEND = "ms" const MESSAGE_RECV = "mr" const WIRE_SEND = "ws" const WIRE_RECV = "wr" const CLIENT_SEND_FRAGMENT = "csf" const CLIENT_RECV_FRAGMENT = "crf" const SERVER_SEND_FRAGMENT = "ssf" const SERVER_RECV_FRAGMENT = "srf" const LOCAL_COMPONENT = "lc" const CLIENT_ADDR = "ca" const SERVER_ADDR = "sa" const MESSAGE_ADDR = "ma" func init() { } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/gen-go/zipkincore/zipkincore.go000066400000000000000000001664411414226744000317740ustar00rootroot00000000000000// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package zipkincore import ( "bytes" "context" "database/sql/driver" "errors" "fmt" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = context.Background var _ = time.Now var _ = bytes.Equal type AnnotationType int64 const ( AnnotationType_BOOL AnnotationType = 0 AnnotationType_BYTES AnnotationType = 1 AnnotationType_I16 AnnotationType = 2 AnnotationType_I32 AnnotationType = 3 AnnotationType_I64 AnnotationType = 4 AnnotationType_DOUBLE AnnotationType = 5 AnnotationType_STRING AnnotationType = 6 ) func (p AnnotationType) String() string { switch p { case AnnotationType_BOOL: return "BOOL" case AnnotationType_BYTES: return "BYTES" case AnnotationType_I16: return "I16" case AnnotationType_I32: return "I32" case AnnotationType_I64: return "I64" case AnnotationType_DOUBLE: return "DOUBLE" case AnnotationType_STRING: return "STRING" } return "" } func AnnotationTypeFromString(s string) (AnnotationType, error) { switch s { case "BOOL": return AnnotationType_BOOL, nil case "BYTES": return AnnotationType_BYTES, nil case "I16": return AnnotationType_I16, nil case "I32": return AnnotationType_I32, nil case "I64": return AnnotationType_I64, nil case "DOUBLE": return AnnotationType_DOUBLE, nil case "STRING": return AnnotationType_STRING, nil } return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") } func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } func (p AnnotationType) MarshalText() ([]byte, error) { return []byte(p.String()), nil } func (p *AnnotationType) UnmarshalText(text []byte) error { q, err := AnnotationTypeFromString(string(text)) if err != nil { return err } *p = q return nil } func (p *AnnotationType) Scan(value interface{}) error { v, ok := value.(int64) if !ok { return errors.New("Scan value is not int64") } *p = AnnotationType(v) return nil } func (p *AnnotationType) Value() (driver.Value, error) { if p == nil { return nil, nil } return int64(*p), nil } // Indicates the network context of a service recording an annotation with two // exceptions. // // When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, // the endpoint indicates the source or destination of an RPC. This exception // allows zipkin to display network context of uninstrumented services, or // clients such as web browsers. // // Attributes: // - Ipv4: IPv4 host address packed into 4 bytes. // // Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 // - Port: IPv4 port // // Note: this is to be treated as an unsigned integer, so watch for negatives. // // Conventionally, when the port isn't known, port = 0. // - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" // // Conventionally, when the service name isn't known, service_name = "unknown". // - Ipv6: IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes() type Endpoint struct { Ipv4 int32 `thrift:"ipv4,1" db:"ipv4" json:"ipv4"` Port int16 `thrift:"port,2" db:"port" json:"port"` ServiceName string `thrift:"service_name,3" db:"service_name" json:"service_name"` Ipv6 []byte `thrift:"ipv6,4" db:"ipv6" json:"ipv6,omitempty"` } func NewEndpoint() *Endpoint { return &Endpoint{} } func (p *Endpoint) GetIpv4() int32 { return p.Ipv4 } func (p *Endpoint) GetPort() int16 { return p.Port } func (p *Endpoint) GetServiceName() string { return p.ServiceName } var Endpoint_Ipv6_DEFAULT []byte func (p *Endpoint) GetIpv6() []byte { return p.Ipv6 } func (p *Endpoint) IsSetIpv6() bool { return p.Ipv6 != nil } func (p *Endpoint) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I32 { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.I16 { if err := p.ReadField2(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.STRING { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.STRING { if err := p.ReadField4(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *Endpoint) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Ipv4 = v } return nil } func (p *Endpoint) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI16(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.Port = v } return nil } func (p *Endpoint) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.ServiceName = v } return nil } func (p *Endpoint) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBinary(ctx); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.Ipv6 = v } return nil } func (p *Endpoint) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Endpoint"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Endpoint) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "ipv4", thrift.I32, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) } if err := oprot.WriteI32(ctx, int32(p.Ipv4)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) } return err } func (p *Endpoint) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "port", thrift.I16, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) } if err := oprot.WriteI16(ctx, int16(p.Port)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) } return err } func (p *Endpoint) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "service_name", thrift.STRING, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) } if err := oprot.WriteString(ctx, string(p.ServiceName)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) } return err } func (p *Endpoint) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetIpv6() { if err := oprot.WriteFieldBegin(ctx, "ipv6", thrift.STRING, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:ipv6: ", p), err) } if err := oprot.WriteBinary(ctx, p.Ipv6); err != nil { return thrift.PrependError(fmt.Sprintf("%T.ipv6 (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:ipv6: ", p), err) } } return err } func (p *Endpoint) Equals(other *Endpoint) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Ipv4 != other.Ipv4 { return false } if p.Port != other.Port { return false } if p.ServiceName != other.ServiceName { return false } if bytes.Compare(p.Ipv6, other.Ipv6) != 0 { return false } return true } func (p *Endpoint) String() string { if p == nil { return "" } return fmt.Sprintf("Endpoint(%+v)", *p) } // An annotation is similar to a log statement. It includes a host field which // allows these events to be attributed properly, and also aggregatable. // // Attributes: // - Timestamp: Microseconds from epoch. // // This value should use the most precise value possible. For example, // gettimeofday or syncing nanoTime against a tick of currentTimeMillis. // - Value // - Host: Always the host that recorded the event. By specifying the host you allow // rollup of all events (such as client requests to a service) by IP address. type Annotation struct { Timestamp int64 `thrift:"timestamp,1" db:"timestamp" json:"timestamp"` Value string `thrift:"value,2" db:"value" json:"value"` Host *Endpoint `thrift:"host,3" db:"host" json:"host,omitempty"` } func NewAnnotation() *Annotation { return &Annotation{} } func (p *Annotation) GetTimestamp() int64 { return p.Timestamp } func (p *Annotation) GetValue() string { return p.Value } var Annotation_Host_DEFAULT *Endpoint func (p *Annotation) GetHost() *Endpoint { if !p.IsSetHost() { return Annotation_Host_DEFAULT } return p.Host } func (p *Annotation) IsSetHost() bool { return p.Host != nil } func (p *Annotation) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I64 { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.STRING { if err := p.ReadField2(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.STRUCT { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *Annotation) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Timestamp = v } return nil } func (p *Annotation) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.Value = v } return nil } func (p *Annotation) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { p.Host = &Endpoint{} if err := p.Host.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) } return nil } func (p *Annotation) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Annotation"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Annotation) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.Timestamp)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) } return err } func (p *Annotation) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "value", thrift.STRING, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } if err := oprot.WriteString(ctx, string(p.Value)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } return err } func (p *Annotation) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetHost() { if err := oprot.WriteFieldBegin(ctx, "host", thrift.STRUCT, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) } if err := p.Host.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) } } return err } func (p *Annotation) Equals(other *Annotation) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Timestamp != other.Timestamp { return false } if p.Value != other.Value { return false } if !p.Host.Equals(other.Host) { return false } return true } func (p *Annotation) String() string { if p == nil { return "" } return fmt.Sprintf("Annotation(%+v)", *p) } // Binary annotations are tags applied to a Span to give it context. For // example, a binary annotation of "http.uri" could the path to a resource in a // RPC call. // // Binary annotations of type STRING are always queryable, though more a // historical implementation detail than a structural concern. // // Binary annotations can repeat, and vary on the host. Similar to Annotation, // the host indicates who logged the event. This allows you to tell the // difference between the client and server side of the same key. For example, // the key "http.uri" might be different on the client and server side due to // rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, // you can see the different points of view, which often help in debugging. // // Attributes: // - Key // - Value // - AnnotationType // - Host: The host that recorded tag, which allows you to differentiate between // multiple tags with the same key. There are two exceptions to this. // // When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or // destination of an RPC. This exception allows zipkin to display network // context of uninstrumented services, or clients such as web browsers. type BinaryAnnotation struct { Key string `thrift:"key,1" db:"key" json:"key"` Value []byte `thrift:"value,2" db:"value" json:"value"` AnnotationType AnnotationType `thrift:"annotation_type,3" db:"annotation_type" json:"annotation_type"` Host *Endpoint `thrift:"host,4" db:"host" json:"host,omitempty"` } func NewBinaryAnnotation() *BinaryAnnotation { return &BinaryAnnotation{} } func (p *BinaryAnnotation) GetKey() string { return p.Key } func (p *BinaryAnnotation) GetValue() []byte { return p.Value } func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { return p.AnnotationType } var BinaryAnnotation_Host_DEFAULT *Endpoint func (p *BinaryAnnotation) GetHost() *Endpoint { if !p.IsSetHost() { return BinaryAnnotation_Host_DEFAULT } return p.Host } func (p *BinaryAnnotation) IsSetHost() bool { return p.Host != nil } func (p *BinaryAnnotation) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.STRING { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 2: if fieldTypeId == thrift.STRING { if err := p.ReadField2(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.I32 { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.STRUCT { if err := p.ReadField4(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *BinaryAnnotation) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Key = v } return nil } func (p *BinaryAnnotation) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBinary(ctx); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.Value = v } return nil } func (p *BinaryAnnotation) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { temp := AnnotationType(v) p.AnnotationType = temp } return nil } func (p *BinaryAnnotation) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { p.Host = &Endpoint{} if err := p.Host.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) } return nil } func (p *BinaryAnnotation) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "BinaryAnnotation"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField2(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *BinaryAnnotation) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "key", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) } if err := oprot.WriteString(ctx, string(p.Key)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) } return err } func (p *BinaryAnnotation) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "value", thrift.STRING, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } if err := oprot.WriteBinary(ctx, p.Value); err != nil { return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } return err } func (p *BinaryAnnotation) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "annotation_type", thrift.I32, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) } if err := oprot.WriteI32(ctx, int32(p.AnnotationType)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) } return err } func (p *BinaryAnnotation) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetHost() { if err := oprot.WriteFieldBegin(ctx, "host", thrift.STRUCT, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) } if err := p.Host.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) } } return err } func (p *BinaryAnnotation) Equals(other *BinaryAnnotation) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Key != other.Key { return false } if bytes.Compare(p.Value, other.Value) != 0 { return false } if p.AnnotationType != other.AnnotationType { return false } if !p.Host.Equals(other.Host) { return false } return true } func (p *BinaryAnnotation) String() string { if p == nil { return "" } return fmt.Sprintf("BinaryAnnotation(%+v)", *p) } // A trace is a series of spans (often RPC calls) which form a latency tree. // // The root span is where trace_id = id and parent_id = Nil. The root span is // usually the longest interval in the trace, starting with a SERVER_RECV // annotation and ending with a SERVER_SEND. // // Attributes: // - TraceID // - Name: Span name in lowercase, rpc method for example // // Conventionally, when the span name isn't known, name = "unknown". // - ID // - ParentID // - Annotations // - BinaryAnnotations // - Debug // - Timestamp: Microseconds from epoch of the creation of this span. // // This value should be set directly by instrumentation, using the most // precise value possible. For example, gettimeofday or syncing nanoTime // against a tick of currentTimeMillis. // // For compatibility with instrumentation that precede this field, collectors // or span stores can derive this via Annotation.timestamp. // For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. // // This field is optional for compatibility with old data: first-party span // stores are expected to support this at time of introduction. // - Duration: Measurement of duration in microseconds, used to support queries. // // This value should be set directly, where possible. Doing so encourages // precise measurement decoupled from problems of clocks, such as skew or NTP // updates causing time to move backwards. // // For compatibility with instrumentation that precede this field, collectors // or span stores can derive this by subtracting Annotation.timestamp. // For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. // // If this field is persisted as unset, zipkin will continue to work, except // duration query support will be implementation-specific. Similarly, setting // this field non-atomically is implementation-specific. // // This field is i64 vs i32 to support spans longer than 35 minutes. // - TraceIDHigh: Optional unique 8-byte additional identifier for a trace. If non zero, this // means the trace uses 128 bit traceIds instead of 64 bit. type Span struct { TraceID int64 `thrift:"trace_id,1" db:"trace_id" json:"trace_id"` // unused field # 2 Name string `thrift:"name,3" db:"name" json:"name"` ID int64 `thrift:"id,4" db:"id" json:"id"` ParentID *int64 `thrift:"parent_id,5" db:"parent_id" json:"parent_id,omitempty"` Annotations []*Annotation `thrift:"annotations,6" db:"annotations" json:"annotations"` // unused field # 7 BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" db:"binary_annotations" json:"binary_annotations"` Debug bool `thrift:"debug,9" db:"debug" json:"debug"` Timestamp *int64 `thrift:"timestamp,10" db:"timestamp" json:"timestamp,omitempty"` Duration *int64 `thrift:"duration,11" db:"duration" json:"duration,omitempty"` TraceIDHigh *int64 `thrift:"trace_id_high,12" db:"trace_id_high" json:"trace_id_high,omitempty"` } func NewSpan() *Span { return &Span{} } func (p *Span) GetTraceID() int64 { return p.TraceID } func (p *Span) GetName() string { return p.Name } func (p *Span) GetID() int64 { return p.ID } var Span_ParentID_DEFAULT int64 func (p *Span) GetParentID() int64 { if !p.IsSetParentID() { return Span_ParentID_DEFAULT } return *p.ParentID } func (p *Span) GetAnnotations() []*Annotation { return p.Annotations } func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { return p.BinaryAnnotations } var Span_Debug_DEFAULT bool = false func (p *Span) GetDebug() bool { return p.Debug } var Span_Timestamp_DEFAULT int64 func (p *Span) GetTimestamp() int64 { if !p.IsSetTimestamp() { return Span_Timestamp_DEFAULT } return *p.Timestamp } var Span_Duration_DEFAULT int64 func (p *Span) GetDuration() int64 { if !p.IsSetDuration() { return Span_Duration_DEFAULT } return *p.Duration } var Span_TraceIDHigh_DEFAULT int64 func (p *Span) GetTraceIDHigh() int64 { if !p.IsSetTraceIDHigh() { return Span_TraceIDHigh_DEFAULT } return *p.TraceIDHigh } func (p *Span) IsSetParentID() bool { return p.ParentID != nil } func (p *Span) IsSetDebug() bool { return p.Debug != Span_Debug_DEFAULT } func (p *Span) IsSetTimestamp() bool { return p.Timestamp != nil } func (p *Span) IsSetDuration() bool { return p.Duration != nil } func (p *Span) IsSetTraceIDHigh() bool { return p.TraceIDHigh != nil } func (p *Span) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.I64 { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 3: if fieldTypeId == thrift.STRING { if err := p.ReadField3(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 4: if fieldTypeId == thrift.I64 { if err := p.ReadField4(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 5: if fieldTypeId == thrift.I64 { if err := p.ReadField5(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 6: if fieldTypeId == thrift.LIST { if err := p.ReadField6(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 8: if fieldTypeId == thrift.LIST { if err := p.ReadField8(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 9: if fieldTypeId == thrift.BOOL { if err := p.ReadField9(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 10: if fieldTypeId == thrift.I64 { if err := p.ReadField10(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 11: if fieldTypeId == thrift.I64 { if err := p.ReadField11(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } case 12: if fieldTypeId == thrift.I64 { if err := p.ReadField12(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *Span) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.TraceID = v } return nil } func (p *Span) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadString(ctx); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.Name = v } return nil } func (p *Span) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.ID = v } return nil } func (p *Span) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 5: ", err) } else { p.ParentID = &v } return nil } func (p *Span) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Annotation, 0, size) p.Annotations = tSlice for i := 0; i < size; i++ { _elem0 := &Annotation{} if err := _elem0.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) } p.Annotations = append(p.Annotations, _elem0) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Span) ReadField8(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*BinaryAnnotation, 0, size) p.BinaryAnnotations = tSlice for i := 0; i < size; i++ { _elem1 := &BinaryAnnotation{} if err := _elem1.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) } p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Span) ReadField9(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBool(ctx); err != nil { return thrift.PrependError("error reading field 9: ", err) } else { p.Debug = v } return nil } func (p *Span) ReadField10(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 10: ", err) } else { p.Timestamp = &v } return nil } func (p *Span) ReadField11(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 11: ", err) } else { p.Duration = &v } return nil } func (p *Span) ReadField12(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(ctx); err != nil { return thrift.PrependError("error reading field 12: ", err) } else { p.TraceIDHigh = &v } return nil } func (p *Span) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Span"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } if err := p.writeField3(ctx, oprot); err != nil { return err } if err := p.writeField4(ctx, oprot); err != nil { return err } if err := p.writeField5(ctx, oprot); err != nil { return err } if err := p.writeField6(ctx, oprot); err != nil { return err } if err := p.writeField8(ctx, oprot); err != nil { return err } if err := p.writeField9(ctx, oprot); err != nil { return err } if err := p.writeField10(ctx, oprot); err != nil { return err } if err := p.writeField11(ctx, oprot); err != nil { return err } if err := p.writeField12(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Span) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "trace_id", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.TraceID)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) } return err } func (p *Span) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "name", thrift.STRING, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) } if err := oprot.WriteString(ctx, string(p.Name)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) } return err } func (p *Span) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "id", thrift.I64, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) } if err := oprot.WriteI64(ctx, int64(p.ID)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) } return err } func (p *Span) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetParentID() { if err := oprot.WriteFieldBegin(ctx, "parent_id", thrift.I64, 5); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.ParentID)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) } } return err } func (p *Span) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "annotations", thrift.LIST, 6); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Annotations)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Annotations { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) } return err } func (p *Span) writeField8(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "binary_annotations", thrift.LIST, 8); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.BinaryAnnotations { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) } return err } func (p *Span) writeField9(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetDebug() { if err := oprot.WriteFieldBegin(ctx, "debug", thrift.BOOL, 9); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) } if err := oprot.WriteBool(ctx, bool(p.Debug)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) } } return err } func (p *Span) writeField10(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetTimestamp() { if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 10); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.Timestamp)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) } } return err } func (p *Span) writeField11(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetDuration() { if err := oprot.WriteFieldBegin(ctx, "duration", thrift.I64, 11); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.Duration)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) } } return err } func (p *Span) writeField12(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetTraceIDHigh() { if err := oprot.WriteFieldBegin(ctx, "trace_id_high", thrift.I64, 12); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 12:trace_id_high: ", p), err) } if err := oprot.WriteI64(ctx, int64(*p.TraceIDHigh)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.trace_id_high (12) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 12:trace_id_high: ", p), err) } } return err } func (p *Span) Equals(other *Span) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.TraceID != other.TraceID { return false } if p.Name != other.Name { return false } if p.ID != other.ID { return false } if p.ParentID != other.ParentID { if p.ParentID == nil || other.ParentID == nil { return false } if (*p.ParentID) != (*other.ParentID) { return false } } if len(p.Annotations) != len(other.Annotations) { return false } for i, _tgt := range p.Annotations { _src2 := other.Annotations[i] if !_tgt.Equals(_src2) { return false } } if len(p.BinaryAnnotations) != len(other.BinaryAnnotations) { return false } for i, _tgt := range p.BinaryAnnotations { _src3 := other.BinaryAnnotations[i] if !_tgt.Equals(_src3) { return false } } if p.Debug != other.Debug { return false } if p.Timestamp != other.Timestamp { if p.Timestamp == nil || other.Timestamp == nil { return false } if (*p.Timestamp) != (*other.Timestamp) { return false } } if p.Duration != other.Duration { if p.Duration == nil || other.Duration == nil { return false } if (*p.Duration) != (*other.Duration) { return false } } if p.TraceIDHigh != other.TraceIDHigh { if p.TraceIDHigh == nil || other.TraceIDHigh == nil { return false } if (*p.TraceIDHigh) != (*other.TraceIDHigh) { return false } } return true } func (p *Span) String() string { if p == nil { return "" } return fmt.Sprintf("Span(%+v)", *p) } // Attributes: // - Ok type Response struct { Ok bool `thrift:"ok,1,required" db:"ok" json:"ok"` } func NewResponse() *Response { return &Response{} } func (p *Response) GetOk() bool { return p.Ok } func (p *Response) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetOk bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.BOOL { if err := p.ReadField1(ctx, iprot); err != nil { return err } issetOk = true } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetOk { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) } return nil } func (p *Response) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { if v, err := iprot.ReadBool(ctx); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Ok = v } return nil } func (p *Response) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "Response"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Response) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "ok", thrift.BOOL, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) } if err := oprot.WriteBool(ctx, bool(p.Ok)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) } return err } func (p *Response) Equals(other *Response) bool { if p == other { return true } else if p == nil || other == nil { return false } if p.Ok != other.Ok { return false } return true } func (p *Response) String() string { if p == nil { return "" } return fmt.Sprintf("Response(%+v)", *p) } type ZipkinCollector interface { // Parameters: // - Spans SubmitZipkinBatch(ctx context.Context, spans []*Span) (_r []*Response, _err error) } type ZipkinCollectorClient struct { c thrift.TClient meta thrift.ResponseMeta } func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { return &ZipkinCollectorClient{ c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), } } func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { return &ZipkinCollectorClient{ c: thrift.NewTStandardClient(iprot, oprot), } } func NewZipkinCollectorClient(c thrift.TClient) *ZipkinCollectorClient { return &ZipkinCollectorClient{ c: c, } } func (p *ZipkinCollectorClient) Client_() thrift.TClient { return p.c } func (p *ZipkinCollectorClient) LastResponseMeta_() thrift.ResponseMeta { return p.meta } func (p *ZipkinCollectorClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { p.meta = meta } // Parameters: // - Spans func (p *ZipkinCollectorClient) SubmitZipkinBatch(ctx context.Context, spans []*Span) (_r []*Response, _err error) { var _args4 ZipkinCollectorSubmitZipkinBatchArgs _args4.Spans = spans var _result6 ZipkinCollectorSubmitZipkinBatchResult var _meta5 thrift.ResponseMeta _meta5, _err = p.Client_().Call(ctx, "submitZipkinBatch", &_args4, &_result6) p.SetLastResponseMeta_(_meta5) if _err != nil { return } return _result6.GetSuccess(), nil } type ZipkinCollectorProcessor struct { processorMap map[string]thrift.TProcessorFunction handler ZipkinCollector } func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { p.processorMap[key] = processor } func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { processor, ok = p.processorMap[key] return processor, ok } func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { return p.processorMap } func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { self7 := &ZipkinCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self7.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler: handler} return self7 } func (p *ZipkinCollectorProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) if err2 != nil { return false, thrift.WrapTException(err2) } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(ctx, seqId, iprot, oprot) } iprot.Skip(ctx, thrift.STRUCT) iprot.ReadMessageEnd(ctx) x8 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) x8.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return false, x8 } type zipkinCollectorProcessorSubmitZipkinBatch struct { handler ZipkinCollector } func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := ZipkinCollectorSubmitZipkinBatchArgs{} var err2 error if err2 = args.Read(ctx, iprot); err2 != nil { iprot.ReadMessageEnd(ctx) x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error()) oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.EXCEPTION, seqId) x.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return false, thrift.WrapTException(err2) } iprot.ReadMessageEnd(ctx) tickerCancel := func() {} // Start a goroutine to do server side connectivity check. if thrift.ServerConnectivityCheckInterval > 0 { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) defer cancel() var tickerCtx context.Context tickerCtx, tickerCancel = context.WithCancel(context.Background()) defer tickerCancel() go func(ctx context.Context, cancel context.CancelFunc) { ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: if !iprot.Transport().IsOpen() { cancel() return } } } }(tickerCtx, cancel) } result := ZipkinCollectorSubmitZipkinBatchResult{} var retval []*Response if retval, err2 = p.handler.SubmitZipkinBatch(ctx, args.Spans); err2 != nil { tickerCancel() if err2 == thrift.ErrAbandonRequest { return false, thrift.WrapTException(err2) } x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: "+err2.Error()) oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.EXCEPTION, seqId) x.Write(ctx, oprot) oprot.WriteMessageEnd(ctx) oprot.Flush(ctx) return true, thrift.WrapTException(err2) } else { result.Success = retval } tickerCancel() if err2 = oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { err = thrift.WrapTException(err2) } if err2 = result.Write(ctx, oprot); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err2 = oprot.Flush(ctx); err == nil && err2 != nil { err = thrift.WrapTException(err2) } if err != nil { return } return true, err } // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Spans type ZipkinCollectorSubmitZipkinBatchArgs struct { Spans []*Span `thrift:"spans,1" db:"spans" json:"spans"` } func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { return &ZipkinCollectorSubmitZipkinBatchArgs{} } func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { return p.Spans } func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if fieldTypeId == thrift.LIST { if err := p.ReadField1(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Span, 0, size) p.Spans = tSlice for i := 0; i < size; i++ { _elem9 := &Span{} if err := _elem9.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem9), err) } p.Spans = append(p.Spans, _elem9) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "submitZipkinBatch_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField1(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Spans { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) } return err } func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { if p == nil { return "" } return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) } // Attributes: // - Success type ZipkinCollectorSubmitZipkinBatchResult struct { Success []*Response `thrift:"success,0" db:"success" json:"success,omitempty"` } func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { return &ZipkinCollectorSubmitZipkinBatchResult{} } var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { return p.Success } func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { return p.Success != nil } func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(ctx context.Context, iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 0: if fieldTypeId == thrift.LIST { if err := p.ReadField0(ctx, iprot); err != nil { return err } } else { if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } default: if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin(ctx) if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Response, 0, size) p.Success = tSlice for i := 0; i < size; i++ { _elem10 := &Response{} if err := _elem10.Read(ctx, iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem10), err) } p.Success = append(p.Success, _elem10) } if err := iprot.ReadListEnd(ctx); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(ctx context.Context, oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin(ctx, "submitZipkinBatch_result"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if p != nil { if err := p.writeField0(ctx, oprot); err != nil { return err } } if err := oprot.WriteFieldStop(ctx); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(ctx); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) { if p.IsSetSuccess() { if err := oprot.WriteFieldBegin(ctx, "success", thrift.LIST, 0); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Success)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Success { if err := v.Write(ctx, oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(ctx); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(ctx); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } } return err } func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { if p == nil { return "" } return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/000077500000000000000000000000001414226744000262545ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/000077500000000000000000000000001414226744000275545ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/LICENSE000066400000000000000000000361601414226744000305670ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. -------------------------------------------------- SOFTWARE DISTRIBUTED WITH THRIFT: The Apache Thrift software includes a number of subcomponents with separate copyright notices and license terms. Your use of the source code for the these subcomponents is subject to the terms and conditions of the following licenses. -------------------------------------------------- Portions of the following files are licensed under the MIT License: lib/erl/src/Makefile.am Please see doc/otp-base-license.txt for the full terms of this license. -------------------------------------------------- For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components: # Copyright (c) 2007 Thomas Porschberg # # Copying and distribution of this file, with or without # modification, are permitted in any medium without royalty provided # the copyright notice and this notice are preserved. -------------------------------------------------- For the lib/nodejs/lib/thrift/json_parse.js: /* json_parse.js 2015-05-02 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. */ (By Douglas Crockford ) -------------------------------------------------- For lib/cpp/src/thrift/windows/SocketPair.cpp /* socketpair.c * Copyright 2007 by Nathan C. Myers ; some rights reserved. * This code is Free Software. It may be copied freely, in original or * modified form, subject only to the restrictions that (1) the author is * relieved from all responsibilities for any use for any purpose, and (2) * this copyright notice must be retained, unchanged, in its entirety. If * for any reason the author might be held responsible for any consequences * of copying or use, license is withheld. */ -------------------------------------------------- For lib/py/compat/win32/stdint.h // ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// -------------------------------------------------- Codegen template in t_html_generator.h * Bootstrap v2.0.3 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. --------------------------------------------------- For t_cl_generator.cc * Copyright (c) 2008- Patrick Collison * Copyright (c) 2006- Facebook --------------------------------------------------- golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/NOTICE000066400000000000000000000002561414226744000304630ustar00rootroot00000000000000Apache Thrift Copyright (C) 2006 - 2019, The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/000077500000000000000000000000001414226744000303225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/000077500000000000000000000000001414226744000307275ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/000077500000000000000000000000001414226744000322275ustar00rootroot00000000000000application_exception.go000066400000000000000000000107221414226744000370620ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" ) const ( UNKNOWN_APPLICATION_EXCEPTION = 0 UNKNOWN_METHOD = 1 INVALID_MESSAGE_TYPE_EXCEPTION = 2 WRONG_METHOD_NAME = 3 BAD_SEQUENCE_ID = 4 MISSING_RESULT = 5 INTERNAL_ERROR = 6 PROTOCOL_ERROR = 7 INVALID_TRANSFORM = 8 INVALID_PROTOCOL = 9 UNSUPPORTED_CLIENT_TYPE = 10 ) var defaultApplicationExceptionMessage = map[int32]string{ UNKNOWN_APPLICATION_EXCEPTION: "unknown application exception", UNKNOWN_METHOD: "unknown method", INVALID_MESSAGE_TYPE_EXCEPTION: "invalid message type", WRONG_METHOD_NAME: "wrong method name", BAD_SEQUENCE_ID: "bad sequence ID", MISSING_RESULT: "missing result", INTERNAL_ERROR: "unknown internal error", PROTOCOL_ERROR: "unknown protocol error", INVALID_TRANSFORM: "Invalid transform", INVALID_PROTOCOL: "Invalid protocol", UNSUPPORTED_CLIENT_TYPE: "Unsupported client type", } // Application level Thrift exception type TApplicationException interface { TException TypeId() int32 Read(ctx context.Context, iprot TProtocol) error Write(ctx context.Context, oprot TProtocol) error } type tApplicationException struct { message string type_ int32 } var _ TApplicationException = (*tApplicationException)(nil) func (tApplicationException) TExceptionType() TExceptionType { return TExceptionTypeApplication } func (e tApplicationException) Error() string { if e.message != "" { return e.message } return defaultApplicationExceptionMessage[e.type_] } func NewTApplicationException(type_ int32, message string) TApplicationException { return &tApplicationException{message, type_} } func (p *tApplicationException) TypeId() int32 { return p.type_ } func (p *tApplicationException) Read(ctx context.Context, iprot TProtocol) error { // TODO: this should really be generated by the compiler _, err := iprot.ReadStructBegin(ctx) if err != nil { return err } message := "" type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) for { _, ttype, id, err := iprot.ReadFieldBegin(ctx) if err != nil { return err } if ttype == STOP { break } switch id { case 1: if ttype == STRING { if message, err = iprot.ReadString(ctx); err != nil { return err } } else { if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } case 2: if ttype == I32 { if type_, err = iprot.ReadI32(ctx); err != nil { return err } } else { if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } default: if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } if err = iprot.ReadFieldEnd(ctx); err != nil { return err } } if err := iprot.ReadStructEnd(ctx); err != nil { return err } p.message = message p.type_ = type_ return nil } func (p *tApplicationException) Write(ctx context.Context, oprot TProtocol) (err error) { err = oprot.WriteStructBegin(ctx, "TApplicationException") if len(p.Error()) > 0 { err = oprot.WriteFieldBegin(ctx, "message", STRING, 1) if err != nil { return } err = oprot.WriteString(ctx, p.Error()) if err != nil { return } err = oprot.WriteFieldEnd(ctx) if err != nil { return } } err = oprot.WriteFieldBegin(ctx, "type", I32, 2) if err != nil { return } err = oprot.WriteI32(ctx, p.type_) if err != nil { return } err = oprot.WriteFieldEnd(ctx) if err != nil { return } err = oprot.WriteFieldStop(ctx) if err != nil { return } err = oprot.WriteStructEnd(ctx) return } binary_protocol.go000066400000000000000000000324611414226744000357120ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "context" "encoding/binary" "errors" "fmt" "io" "math" ) type TBinaryProtocol struct { trans TRichTransport origTransport TTransport cfg *TConfiguration buffer [64]byte } type TBinaryProtocolFactory struct { cfg *TConfiguration } // Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { return NewTBinaryProtocolConf(t, &TConfiguration{ noPropagation: true, }) } // Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { return NewTBinaryProtocolConf(t, &TConfiguration{ TBinaryStrictRead: &strictRead, TBinaryStrictWrite: &strictWrite, noPropagation: true, }) } func NewTBinaryProtocolConf(t TTransport, conf *TConfiguration) *TBinaryProtocol { PropagateTConfiguration(t, conf) p := &TBinaryProtocol{ origTransport: t, cfg: conf, } if et, ok := t.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(t) } return p } // Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { return NewTBinaryProtocolFactoryConf(&TConfiguration{ noPropagation: true, }) } // Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { return NewTBinaryProtocolFactoryConf(&TConfiguration{ TBinaryStrictRead: &strictRead, TBinaryStrictWrite: &strictWrite, noPropagation: true, }) } func NewTBinaryProtocolFactoryConf(conf *TConfiguration) *TBinaryProtocolFactory { return &TBinaryProtocolFactory{ cfg: conf, } } func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { return NewTBinaryProtocolConf(t, p.cfg) } func (p *TBinaryProtocolFactory) SetTConfiguration(conf *TConfiguration) { p.cfg = conf } /** * Writing Methods */ func (p *TBinaryProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { if p.cfg.GetTBinaryStrictWrite() { version := uint32(VERSION_1) | uint32(typeId) e := p.WriteI32(ctx, int32(version)) if e != nil { return e } e = p.WriteString(ctx, name) if e != nil { return e } e = p.WriteI32(ctx, seqId) return e } else { e := p.WriteString(ctx, name) if e != nil { return e } e = p.WriteByte(ctx, int8(typeId)) if e != nil { return e } e = p.WriteI32(ctx, seqId) return e } return nil } func (p *TBinaryProtocol) WriteMessageEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteStructBegin(ctx context.Context, name string) error { return nil } func (p *TBinaryProtocol) WriteStructEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { e := p.WriteByte(ctx, int8(typeId)) if e != nil { return e } e = p.WriteI16(ctx, id) return e } func (p *TBinaryProtocol) WriteFieldEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteFieldStop(ctx context.Context) error { e := p.WriteByte(ctx, STOP) return e } func (p *TBinaryProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { e := p.WriteByte(ctx, int8(keyType)) if e != nil { return e } e = p.WriteByte(ctx, int8(valueType)) if e != nil { return e } e = p.WriteI32(ctx, int32(size)) return e } func (p *TBinaryProtocol) WriteMapEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } e = p.WriteI32(ctx, int32(size)) return e } func (p *TBinaryProtocol) WriteListEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } e = p.WriteI32(ctx, int32(size)) return e } func (p *TBinaryProtocol) WriteSetEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) WriteBool(ctx context.Context, value bool) error { if value { return p.WriteByte(ctx, 1) } return p.WriteByte(ctx, 0) } func (p *TBinaryProtocol) WriteByte(ctx context.Context, value int8) error { e := p.trans.WriteByte(byte(value)) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI16(ctx context.Context, value int16) error { v := p.buffer[0:2] binary.BigEndian.PutUint16(v, uint16(value)) _, e := p.trans.Write(v) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI32(ctx context.Context, value int32) error { v := p.buffer[0:4] binary.BigEndian.PutUint32(v, uint32(value)) _, e := p.trans.Write(v) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI64(ctx context.Context, value int64) error { v := p.buffer[0:8] binary.BigEndian.PutUint64(v, uint64(value)) _, err := p.trans.Write(v) return NewTProtocolException(err) } func (p *TBinaryProtocol) WriteDouble(ctx context.Context, value float64) error { return p.WriteI64(ctx, int64(math.Float64bits(value))) } func (p *TBinaryProtocol) WriteString(ctx context.Context, value string) error { e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } _, err := p.trans.WriteString(value) return NewTProtocolException(err) } func (p *TBinaryProtocol) WriteBinary(ctx context.Context, value []byte) error { e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } _, err := p.trans.Write(value) return NewTProtocolException(err) } /** * Reading methods */ func (p *TBinaryProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { size, e := p.ReadI32(ctx) if e != nil { return "", typeId, 0, NewTProtocolException(e) } if size < 0 { typeId = TMessageType(size & 0x0ff) version := int64(int64(size) & VERSION_MASK) if version != VERSION_1 { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin")) } name, e = p.ReadString(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } seqId, e = p.ReadI32(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } return name, typeId, seqId, nil } if p.cfg.GetTBinaryStrictRead() { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin")) } name, e2 := p.readStringBody(size) if e2 != nil { return name, typeId, seqId, e2 } b, e3 := p.ReadByte(ctx) if e3 != nil { return name, typeId, seqId, e3 } typeId = TMessageType(b) seqId, e4 := p.ReadI32(ctx) if e4 != nil { return name, typeId, seqId, e4 } return name, typeId, seqId, nil } func (p *TBinaryProtocol) ReadMessageEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { return } func (p *TBinaryProtocol) ReadStructEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, seqId int16, err error) { t, err := p.ReadByte(ctx) typeId = TType(t) if err != nil { return name, typeId, seqId, err } if t != STOP { seqId, err = p.ReadI16(ctx) } return name, typeId, seqId, err } func (p *TBinaryProtocol) ReadFieldEnd(ctx context.Context) error { return nil } var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length")) func (p *TBinaryProtocol) ReadMapBegin(ctx context.Context) (kType, vType TType, size int, err error) { k, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } kType = TType(k) v, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } vType = TType(v) size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return kType, vType, size, nil } func (p *TBinaryProtocol) ReadMapEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return } func (p *TBinaryProtocol) ReadListEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return elemType, size, nil } func (p *TBinaryProtocol) ReadSetEnd(ctx context.Context) error { return nil } func (p *TBinaryProtocol) ReadBool(ctx context.Context) (bool, error) { b, e := p.ReadByte(ctx) v := true if b != 1 { v = false } return v, e } func (p *TBinaryProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.trans.ReadByte() return int8(v), err } func (p *TBinaryProtocol) ReadI16(ctx context.Context) (value int16, err error) { buf := p.buffer[0:2] err = p.readAll(ctx, buf) value = int16(binary.BigEndian.Uint16(buf)) return value, err } func (p *TBinaryProtocol) ReadI32(ctx context.Context) (value int32, err error) { buf := p.buffer[0:4] err = p.readAll(ctx, buf) value = int32(binary.BigEndian.Uint32(buf)) return value, err } func (p *TBinaryProtocol) ReadI64(ctx context.Context) (value int64, err error) { buf := p.buffer[0:8] err = p.readAll(ctx, buf) value = int64(binary.BigEndian.Uint64(buf)) return value, err } func (p *TBinaryProtocol) ReadDouble(ctx context.Context) (value float64, err error) { buf := p.buffer[0:8] err = p.readAll(ctx, buf) value = math.Float64frombits(binary.BigEndian.Uint64(buf)) return value, err } func (p *TBinaryProtocol) ReadString(ctx context.Context) (value string, err error) { size, e := p.ReadI32(ctx) if e != nil { return "", e } err = checkSizeForProtocol(size, p.cfg) if err != nil { return } if size < 0 { err = invalidDataLength return } if size == 0 { return "", nil } if size < int32(len(p.buffer)) { // Avoid allocation on small reads buf := p.buffer[:size] read, e := io.ReadFull(p.trans, buf) return string(buf[:read]), NewTProtocolException(e) } return p.readStringBody(size) } func (p *TBinaryProtocol) ReadBinary(ctx context.Context) ([]byte, error) { size, e := p.ReadI32(ctx) if e != nil { return nil, e } if err := checkSizeForProtocol(size, p.cfg); err != nil { return nil, err } buf, err := safeReadBytes(size, p.trans) return buf, NewTProtocolException(err) } func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.trans.Flush(ctx)) } func (p *TBinaryProtocol) Skip(ctx context.Context, fieldType TType) (err error) { return SkipDefaultDepth(ctx, p, fieldType) } func (p *TBinaryProtocol) Transport() TTransport { return p.origTransport } func (p *TBinaryProtocol) readAll(ctx context.Context, buf []byte) (err error) { var read int _, deadlineSet := ctx.Deadline() for { read, err = io.ReadFull(p.trans, buf) if deadlineSet && read == 0 && isTimeoutError(err) && ctx.Err() == nil { // This is I/O timeout without anything read, // and we still have time left, keep retrying. continue } // For anything else, don't retry break } return NewTProtocolException(err) } func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) { buf, err := safeReadBytes(size, p.trans) return string(buf), NewTProtocolException(err) } func (p *TBinaryProtocol) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(p.trans, conf) PropagateTConfiguration(p.origTransport, conf) p.cfg = conf } var ( _ TConfigurationSetter = (*TBinaryProtocolFactory)(nil) _ TConfigurationSetter = (*TBinaryProtocol)(nil) ) // This function is shared between TBinaryProtocol and TCompactProtocol. // // It tries to read size bytes from trans, in a way that prevents large // allocations when size is insanely large (mostly caused by malformed message). func safeReadBytes(size int32, trans io.Reader) ([]byte, error) { if size < 0 { return nil, nil } buf := new(bytes.Buffer) _, err := io.CopyN(buf, trans, int64(size)) return buf.Bytes(), err } buffered_transport.go000066400000000000000000000050401414226744000363740ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "context" ) type TBufferedTransportFactory struct { size int } type TBufferedTransport struct { bufio.ReadWriter tp TTransport } func (p *TBufferedTransportFactory) GetTransport(trans TTransport) (TTransport, error) { return NewTBufferedTransport(trans, p.size), nil } func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory { return &TBufferedTransportFactory{size: bufferSize} } func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport { return &TBufferedTransport{ ReadWriter: bufio.ReadWriter{ Reader: bufio.NewReaderSize(trans, bufferSize), Writer: bufio.NewWriterSize(trans, bufferSize), }, tp: trans, } } func (p *TBufferedTransport) IsOpen() bool { return p.tp.IsOpen() } func (p *TBufferedTransport) Open() (err error) { return p.tp.Open() } func (p *TBufferedTransport) Close() (err error) { return p.tp.Close() } func (p *TBufferedTransport) Read(b []byte) (int, error) { n, err := p.ReadWriter.Read(b) if err != nil { p.ReadWriter.Reader.Reset(p.tp) } return n, err } func (p *TBufferedTransport) Write(b []byte) (int, error) { n, err := p.ReadWriter.Write(b) if err != nil { p.ReadWriter.Writer.Reset(p.tp) } return n, err } func (p *TBufferedTransport) Flush(ctx context.Context) error { if err := p.ReadWriter.Flush(); err != nil { p.ReadWriter.Writer.Reset(p.tp) return err } return p.tp.Flush(ctx) } func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) { return p.tp.RemainingBytes() } // SetTConfiguration implements TConfigurationSetter for propagation. func (p *TBufferedTransport) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(p.tp, conf) } var _ TConfigurationSetter = (*TBufferedTransport)(nil) golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/client.go000066400000000000000000000055541414226744000340450ustar00rootroot00000000000000package thrift import ( "context" "fmt" ) // ResponseMeta represents the metadata attached to the response. type ResponseMeta struct { // The headers in the response, if any. // If the underlying transport/protocol is not THeader, this will always be nil. Headers THeaderMap } type TClient interface { Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) } type TStandardClient struct { seqId int32 iprot, oprot TProtocol } // TStandardClient implements TClient, and uses the standard message format for Thrift. // It is not safe for concurrent use. func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClient { return &TStandardClient{ iprot: inputProtocol, oprot: outputProtocol, } } func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error { // Set headers from context object on THeaderProtocol if headerProt, ok := oprot.(*THeaderProtocol); ok { headerProt.ClearWriteHeaders() for _, key := range GetWriteHeaderList(ctx) { if value, ok := GetHeader(ctx, key); ok { headerProt.SetWriteHeader(key, value) } } } if err := oprot.WriteMessageBegin(ctx, method, CALL, seqId); err != nil { return err } if err := args.Write(ctx, oprot); err != nil { return err } if err := oprot.WriteMessageEnd(ctx); err != nil { return err } return oprot.Flush(ctx) } func (p *TStandardClient) Recv(ctx context.Context, iprot TProtocol, seqId int32, method string, result TStruct) error { rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin(ctx) if err != nil { return err } if method != rMethod { return NewTApplicationException(WRONG_METHOD_NAME, fmt.Sprintf("%s: wrong method name", method)) } else if seqId != rSeqId { return NewTApplicationException(BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method)) } else if rTypeId == EXCEPTION { var exception tApplicationException if err := exception.Read(ctx, iprot); err != nil { return err } if err := iprot.ReadMessageEnd(ctx); err != nil { return err } return &exception } else if rTypeId != REPLY { return NewTApplicationException(INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method)) } if err := result.Read(ctx, iprot); err != nil { return err } return iprot.ReadMessageEnd(ctx) } func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { p.seqId++ seqId := p.seqId if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil { return ResponseMeta{}, err } // method is oneway if result == nil { return ResponseMeta{}, nil } err := p.Recv(ctx, p.iprot, seqId, method, result) var headers THeaderMap if hp, ok := p.iprot.(*THeaderProtocol); ok { headers = hp.transport.readHeaders } return ResponseMeta{ Headers: headers, }, err } compact_protocol.go000066400000000000000000000604311414226744000360520ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "encoding/binary" "errors" "fmt" "io" "math" ) const ( COMPACT_PROTOCOL_ID = 0x082 COMPACT_VERSION = 1 COMPACT_VERSION_MASK = 0x1f COMPACT_TYPE_MASK = 0x0E0 COMPACT_TYPE_BITS = 0x07 COMPACT_TYPE_SHIFT_AMOUNT = 5 ) type tCompactType byte const ( COMPACT_BOOLEAN_TRUE = 0x01 COMPACT_BOOLEAN_FALSE = 0x02 COMPACT_BYTE = 0x03 COMPACT_I16 = 0x04 COMPACT_I32 = 0x05 COMPACT_I64 = 0x06 COMPACT_DOUBLE = 0x07 COMPACT_BINARY = 0x08 COMPACT_LIST = 0x09 COMPACT_SET = 0x0A COMPACT_MAP = 0x0B COMPACT_STRUCT = 0x0C ) var ( ttypeToCompactType map[TType]tCompactType ) func init() { ttypeToCompactType = map[TType]tCompactType{ STOP: STOP, BOOL: COMPACT_BOOLEAN_TRUE, BYTE: COMPACT_BYTE, I16: COMPACT_I16, I32: COMPACT_I32, I64: COMPACT_I64, DOUBLE: COMPACT_DOUBLE, STRING: COMPACT_BINARY, LIST: COMPACT_LIST, SET: COMPACT_SET, MAP: COMPACT_MAP, STRUCT: COMPACT_STRUCT, } } type TCompactProtocolFactory struct { cfg *TConfiguration } // Deprecated: Use NewTCompactProtocolFactoryConf instead. func NewTCompactProtocolFactory() *TCompactProtocolFactory { return NewTCompactProtocolFactoryConf(&TConfiguration{ noPropagation: true, }) } func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory { return &TCompactProtocolFactory{ cfg: conf, } } func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTCompactProtocolConf(trans, p.cfg) } func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) { p.cfg = conf } type TCompactProtocol struct { trans TRichTransport origTransport TTransport cfg *TConfiguration // Used to keep track of the last field for the current and previous structs, // so we can do the delta stuff. lastField []int lastFieldId int // If we encounter a boolean field begin, save the TField here so it can // have the value incorporated. booleanFieldName string booleanFieldId int16 booleanFieldPending bool // If we read a field header, and it's a boolean field, save the boolean // value here so that readBool can use it. boolValue bool boolValueIsNotNull bool buffer [64]byte } // Deprecated: Use NewTCompactProtocolConf instead. func NewTCompactProtocol(trans TTransport) *TCompactProtocol { return NewTCompactProtocolConf(trans, &TConfiguration{ noPropagation: true, }) } func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol { PropagateTConfiguration(trans, conf) p := &TCompactProtocol{ origTransport: trans, cfg: conf, } if et, ok := trans.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(trans) } return p } // // Public Writing methods. // // Write a message header to the wire. Compact Protocol messages contain the // protocol version so we can migrate forwards in the future if need be. func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { err := p.writeByteDirect(COMPACT_PROTOCOL_ID) if err != nil { return NewTProtocolException(err) } err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK)) if err != nil { return NewTProtocolException(err) } _, err = p.writeVarint32(seqid) if err != nil { return NewTProtocolException(err) } e := p.WriteString(ctx, name) return e } func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil } // Write a struct begin. This doesn't actually put anything on the wire. We // use it as an opportunity to put special placeholder markers on the field // stack so we can get the field id deltas correct. func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return nil } // Write a struct end. This doesn't actually put anything on the wire. We use // this as an opportunity to pop the last field from the current struct off // of the field stack. func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error { if len(p.lastField) <= 0 { return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before")) } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { if typeId == BOOL { // we want to possibly include the value, so we'll wait. p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true return nil } _, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF) return NewTProtocolException(err) } // The workhorse of writeFieldBegin. It has the option of doing a // 'type override' of the type header. This is used specifically in the // boolean field case. func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) { // short lastField = lastField_.pop(); // if there's a type override, use that. var typeToWrite byte if typeOverride == 0xFF { typeToWrite = byte(p.getCompactType(typeId)) } else { typeToWrite = typeOverride } // check if we can use delta encoding for the field id fieldId := int(id) written := 0 if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 { // write them together err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite) if err != nil { return 0, err } } else { // write them separate err := p.writeByteDirect(typeToWrite) if err != nil { return 0, err } err = p.WriteI16(ctx, id) written = 1 + 2 if err != nil { return 0, err } } p.lastFieldId = fieldId return written, nil } func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil } func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error { err := p.writeByteDirect(STOP) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if size == 0 { err := p.writeByteDirect(0) return NewTProtocolException(err) } _, err := p.writeVarint32(int32(size)) if err != nil { return NewTProtocolException(err) } err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType))) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil } // Write a list header. func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil } // Write a set header. func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil } func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error { v := byte(COMPACT_BOOLEAN_FALSE) if value { v = byte(COMPACT_BOOLEAN_TRUE) } if p.booleanFieldPending { // we haven't written the field header yet _, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v) p.booleanFieldPending = false return NewTProtocolException(err) } // we're not part of a field, so just write the value. err := p.writeByteDirect(v) return NewTProtocolException(err) } // Write a byte. Nothing to see here! func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error { err := p.writeByteDirect(byte(value)) return NewTProtocolException(err) } // Write an I16 as a zigzag varint. func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error { _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) return NewTProtocolException(err) } // Write an i32 as a zigzag varint. func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error { _, err := p.writeVarint32(p.int32ToZigzag(value)) return NewTProtocolException(err) } // Write an i64 as a zigzag varint. func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error { _, err := p.writeVarint64(p.int64ToZigzag(value)) return NewTProtocolException(err) } // Write a double to the wire as 8 bytes. func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error { buf := p.buffer[0:8] binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) _, err := p.trans.Write(buf) return NewTProtocolException(err) } // Write a string to the wire with a varint size preceding. func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error { _, e := p.writeVarint32(int32(len(value))) if e != nil { return NewTProtocolException(e) } if len(value) > 0 { } _, e = p.trans.WriteString(value) return e } // Write a byte array, using a varint for the size. func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error { _, e := p.writeVarint32(int32(len(bin))) if e != nil { return NewTProtocolException(e) } if len(bin) > 0 { _, e = p.trans.Write(bin) return NewTProtocolException(e) } return nil } // // Reading methods. // // Read a message header. func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { var protocolId byte _, deadlineSet := ctx.Deadline() for { protocolId, err = p.readByteDirect() if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { // keep retrying I/O timeout errors since we still have // time left continue } // For anything else, don't retry break } if err != nil { return } if protocolId != COMPACT_PROTOCOL_ID { e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId) return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e) } versionAndType, err := p.readByteDirect() if err != nil { return } version := versionAndType & COMPACT_VERSION_MASK typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS) if version != COMPACT_VERSION { e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version) err = NewTProtocolExceptionWithType(BAD_VERSION, e) return } seqId, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } name, err = p.ReadString(ctx) return } func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil } // Read a struct begin. There's nothing on the wire for this, but it is our // opportunity to push a new struct begin marker onto the field stack. func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return } // Doesn't actually consume any wire data, just removes the last field for // this struct from the field stack. func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error { // consume the last field we read off the wire. if len(p.lastField) <= 0 { return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before")) } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } // Read a field header off the wire. func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) { t, err := p.readByteDirect() if err != nil { return } // if it's a stop, then we can return immediately, as the struct is over. if (t & 0x0f) == STOP { return "", STOP, 0, nil } // mask off the 4 MSB of the type header. it could contain a field id delta. modifier := int16((t & 0xf0) >> 4) if modifier == 0 { // not a delta. look ahead for the zigzag varint field id. id, err = p.ReadI16(ctx) if err != nil { return } } else { // has a delta. add the delta to the last read field id. id = int16(p.lastFieldId) + modifier } typeId, e := p.getTType(tCompactType(t & 0x0f)) if e != nil { err = NewTProtocolException(e) return } // if this happens to be a boolean field, the value is encoded in the type if p.isBoolType(t) { // save the boolean value in a special instance variable. p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE) p.boolValueIsNotNull = true } // push the new field onto the field stack so we can keep the deltas going. p.lastFieldId = int(id) return } func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil } // Read a map header off the wire. If the size is zero, skip reading the key // and value type. This means that 0-length maps will yield TMaps without the // "correct" types. func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { size32, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) keyAndValueType := byte(STOP) if size != 0 { keyAndValueType, err = p.readByteDirect() if err != nil { return } } keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4)) valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf)) return } func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil } // Read a list header off the wire. If the list size is 0-14, the size will // be packed into the element type header. If it's a longer list, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { size_and_type, err := p.readByteDirect() if err != nil { return } size = int((size_and_type >> 4) & 0x0f) if size == 15 { size2, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } if size2 < 0 { err = invalidDataLength return } size = int(size2) } elemType, e := p.getTType(tCompactType(size_and_type)) if e != nil { err = NewTProtocolException(e) return } return } func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil } // Read a set header off the wire. If the set size is 0-14, the size will // be packed into the element type header. If it's a longer set, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { return p.ReadListBegin(ctx) } func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil } // Read a boolean off the wire. If this is a boolean field, the value should // already have been read during readFieldBegin, so we'll just consume the // pre-stored value. Otherwise, read a byte. func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) { if p.boolValueIsNotNull { p.boolValueIsNotNull = false return p.boolValue, nil } v, err := p.readByteDirect() return v == COMPACT_BOOLEAN_TRUE, err } // Read a single byte off the wire. Nothing interesting here. func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.readByteDirect() if err != nil { return 0, NewTProtocolException(err) } return int8(v), err } // Read an i16 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) { v, err := p.ReadI32(ctx) return int16(v), err } // Read an i32 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) { v, e := p.readVarint32() if e != nil { return 0, NewTProtocolException(e) } value = p.zigzagToInt32(v) return value, nil } // Read an i64 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) { v, e := p.readVarint64() if e != nil { return 0, NewTProtocolException(e) } value = p.zigzagToInt64(v) return value, nil } // No magic here - just read a double off the wire. func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) { longBits := p.buffer[0:8] _, e := io.ReadFull(p.trans, longBits) if e != nil { return 0.0, NewTProtocolException(e) } return math.Float64frombits(p.bytesToUint64(longBits)), nil } // Reads a []byte (via readBinary), and then UTF-8 decodes it. func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) { length, e := p.readVarint32() if e != nil { return "", NewTProtocolException(e) } err = checkSizeForProtocol(length, p.cfg) if err != nil { return } if length == 0 { return "", nil } if length < int32(len(p.buffer)) { // Avoid allocation on small reads buf := p.buffer[:length] read, e := io.ReadFull(p.trans, buf) return string(buf[:read]), NewTProtocolException(e) } buf, e := safeReadBytes(length, p.trans) return string(buf), NewTProtocolException(e) } // Read a []byte from the wire. func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { length, e := p.readVarint32() if e != nil { return nil, NewTProtocolException(e) } err = checkSizeForProtocol(length, p.cfg) if err != nil { return } if length == 0 { return []byte{}, nil } buf, e := safeReadBytes(length, p.trans) return buf, NewTProtocolException(e) } func (p *TCompactProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.trans.Flush(ctx)) } func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) { return SkipDefaultDepth(ctx, p, fieldType) } func (p *TCompactProtocol) Transport() TTransport { return p.origTransport } // // Internal writing methods // // Abstract method for writing the start of lists and sets. List and sets on // the wire differ only by the type indicator. func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) { if size <= 14 { return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType)))) } err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType))) if err != nil { return 0, err } m, err := p.writeVarint32(int32(size)) return 1 + m, err } // Write an i32 as a varint. Results in 1-5 bytes on the wire. // TODO(pomack): make a permanent buffer like writeVarint64? func (p *TCompactProtocol) writeVarint32(n int32) (int, error) { i32buf := p.buffer[0:5] idx := 0 for { if (n & ^0x7F) == 0 { i32buf[idx] = byte(n) idx++ // p.writeByteDirect(byte(n)); break // return; } else { i32buf[idx] = byte((n & 0x7F) | 0x80) idx++ // p.writeByteDirect(byte(((n & 0x7F) | 0x80))); u := uint32(n) n = int32(u >> 7) } } return p.trans.Write(i32buf[0:idx]) } // Write an i64 as a varint. Results in 1-10 bytes on the wire. func (p *TCompactProtocol) writeVarint64(n int64) (int, error) { varint64out := p.buffer[0:10] idx := 0 for { if (n & ^0x7F) == 0 { varint64out[idx] = byte(n) idx++ break } else { varint64out[idx] = byte((n & 0x7F) | 0x80) idx++ u := uint64(n) n = int64(u >> 7) } } return p.trans.Write(varint64out[0:idx]) } // Convert l into a zigzag long. This allows negative numbers to be // represented compactly as a varint. func (p *TCompactProtocol) int64ToZigzag(l int64) int64 { return (l << 1) ^ (l >> 63) } // Convert l into a zigzag long. This allows negative numbers to be // represented compactly as a varint. func (p *TCompactProtocol) int32ToZigzag(n int32) int32 { return (n << 1) ^ (n >> 31) } func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) { binary.LittleEndian.PutUint64(buf, n) } func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) { binary.LittleEndian.PutUint64(buf, uint64(n)) } // Writes a byte without any possibility of all that field header nonsense. // Used internally by other writing methods that know they need to write a byte. func (p *TCompactProtocol) writeByteDirect(b byte) error { return p.trans.WriteByte(b) } // Writes a byte without any possibility of all that field header nonsense. func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) { return 1, p.writeByteDirect(byte(n)) } // // Internal reading methods // // Read an i32 from the wire as a varint. The MSB of each byte is set // if there is another byte to follow. This can read up to 5 bytes. func (p *TCompactProtocol) readVarint32() (int32, error) { // if the wire contains the right stuff, this will just truncate the i64 we // read and get us the right sign. v, err := p.readVarint64() return int32(v), err } // Read an i64 from the wire as a proper varint. The MSB of each byte is set // if there is another byte to follow. This can read up to 10 bytes. func (p *TCompactProtocol) readVarint64() (int64, error) { shift := uint(0) result := int64(0) for { b, err := p.readByteDirect() if err != nil { return 0, err } result |= int64(b&0x7f) << shift if (b & 0x80) != 0x80 { break } shift += 7 } return result, nil } // Read a byte, unlike ReadByte that reads Thrift-byte that is i8. func (p *TCompactProtocol) readByteDirect() (byte, error) { return p.trans.ReadByte() } // // encoding helpers // // Convert from zigzag int to int. func (p *TCompactProtocol) zigzagToInt32(n int32) int32 { u := uint32(n) return int32(u>>1) ^ -(n & 1) } // Convert from zigzag long to long. func (p *TCompactProtocol) zigzagToInt64(n int64) int64 { u := uint64(n) return int64(u>>1) ^ -(n & 1) } // Note that it's important that the mask bytes are long literals, // otherwise they'll default to ints, and when you shift an int left 56 bits, // you just get a messed up int. func (p *TCompactProtocol) bytesToInt64(b []byte) int64 { return int64(binary.LittleEndian.Uint64(b)) } // Note that it's important that the mask bytes are long literals, // otherwise they'll default to ints, and when you shift an int left 56 bits, // you just get a messed up int. func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } // // type testing and converting // func (p *TCompactProtocol) isBoolType(b byte) bool { return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE } // Given a tCompactType constant, convert it to its corresponding // TType value. func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) { switch byte(t) & 0x0f { case STOP: return STOP, nil case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE: return BOOL, nil case COMPACT_BYTE: return BYTE, nil case COMPACT_I16: return I16, nil case COMPACT_I32: return I32, nil case COMPACT_I64: return I64, nil case COMPACT_DOUBLE: return DOUBLE, nil case COMPACT_BINARY: return STRING, nil case COMPACT_LIST: return LIST, nil case COMPACT_SET: return SET, nil case COMPACT_MAP: return MAP, nil case COMPACT_STRUCT: return STRUCT, nil } return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f)) } // Given a TType value, find the appropriate TCompactProtocol.Types constant. func (p *TCompactProtocol) getCompactType(t TType) tCompactType { return ttypeToCompactType[t] } func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(p.trans, conf) PropagateTConfiguration(p.origTransport, conf) p.cfg = conf } var ( _ TConfigurationSetter = (*TCompactProtocolFactory)(nil) _ TConfigurationSetter = (*TCompactProtocol)(nil) ) configuration.go000066400000000000000000000277071414226744000353630ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "crypto/tls" "fmt" "time" ) // Default TConfiguration values. const ( DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024 DEFAULT_MAX_FRAME_SIZE = 16384000 DEFAULT_TBINARY_STRICT_READ = false DEFAULT_TBINARY_STRICT_WRITE = true DEFAULT_CONNECT_TIMEOUT = 0 DEFAULT_SOCKET_TIMEOUT = 0 ) // TConfiguration defines some configurations shared between TTransport, // TProtocol, TTransportFactory, TProtocolFactory, and other implementations. // // When constructing TConfiguration, you only need to specify the non-default // fields. All zero values have sane default values. // // Not all configurations defined are applicable to all implementations. // Implementations are free to ignore the configurations not applicable to them. // // All functions attached to this type are nil-safe. // // See [1] for spec. // // NOTE: When using TConfiguration, fill in all the configurations you want to // set across the stack, not only the ones you want to set in the immediate // TTransport/TProtocol. // // For example, say you want to migrate this old code into using TConfiguration: // // sccket := thrift.NewTSocketTimeout("host:port", time.Second) // transFactory := thrift.NewTFramedTransportFactoryMaxLength( // thrift.NewTTransportFactory(), // 1024 * 1024 * 256, // ) // protoFactory := thrift.NewTBinaryProtocolFactory(true, true) // // This is the wrong way to do it because in the end the TConfiguration used by // socket and transFactory will be overwritten by the one used by protoFactory // because of TConfiguration propagation: // // // bad example, DO NOT USE // sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{ // ConnectTimeout: time.Second, // SocketTimeout: time.Second, // }) // transFactory := thrift.NewTFramedTransportFactoryConf( // thrift.NewTTransportFactory(), // &thrift.TConfiguration{ // MaxFrameSize: 1024 * 1024 * 256, // }, // ) // protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{ // TBinaryStrictRead: thrift.BoolPtr(true), // TBinaryStrictWrite: thrift.BoolPtr(true), // }) // // This is the correct way to do it: // // conf := &thrift.TConfiguration{ // ConnectTimeout: time.Second, // SocketTimeout: time.Second, // // MaxFrameSize: 1024 * 1024 * 256, // // TBinaryStrictRead: thrift.BoolPtr(true), // TBinaryStrictWrite: thrift.BoolPtr(true), // } // sccket := thrift.NewTSocketConf("host:port", conf) // transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf) // protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf) // // [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md type TConfiguration struct { // If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead. MaxMessageSize int32 // If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead. // // Also if MaxMessageSize < MaxFrameSize, // MaxMessageSize will be used instead. MaxFrameSize int32 // Connect and socket timeouts to be used by TSocket and TSSLSocket. // // 0 means no timeout. // // If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be // used. ConnectTimeout time.Duration SocketTimeout time.Duration // TLS config to be used by TSSLSocket. TLSConfig *tls.Config // Strict read/write configurations for TBinaryProtocol. // // BoolPtr helper function is available to use literal values. TBinaryStrictRead *bool TBinaryStrictWrite *bool // The wrapped protocol id to be used in THeader transport/protocol. // // THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions // are provided to help filling this value. THeaderProtocolID *THeaderProtocolID // Used internally by deprecated constructors, to avoid overriding // underlying TTransport/TProtocol's cfg by accidental propagations. // // For external users this is always false. noPropagation bool } // GetMaxMessageSize returns the max message size an implementation should // follow. // // It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil. func (tc *TConfiguration) GetMaxMessageSize() int32 { if tc == nil || tc.MaxMessageSize <= 0 { return DEFAULT_MAX_MESSAGE_SIZE } return tc.MaxMessageSize } // GetMaxFrameSize returns the max frame size an implementation should follow. // // It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil. // // If the configured max message size is smaller than the configured max frame // size, the smaller one will be returned instead. func (tc *TConfiguration) GetMaxFrameSize() int32 { if tc == nil { return DEFAULT_MAX_FRAME_SIZE } maxFrameSize := tc.MaxFrameSize if maxFrameSize <= 0 { maxFrameSize = DEFAULT_MAX_FRAME_SIZE } if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize { return maxMessageSize } return maxFrameSize } // GetConnectTimeout returns the connect timeout should be used by TSocket and // TSSLSocket. // // It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead. func (tc *TConfiguration) GetConnectTimeout() time.Duration { if tc == nil || tc.ConnectTimeout < 0 { return DEFAULT_CONNECT_TIMEOUT } return tc.ConnectTimeout } // GetSocketTimeout returns the socket timeout should be used by TSocket and // TSSLSocket. // // It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead. func (tc *TConfiguration) GetSocketTimeout() time.Duration { if tc == nil || tc.SocketTimeout < 0 { return DEFAULT_SOCKET_TIMEOUT } return tc.SocketTimeout } // GetTLSConfig returns the tls config should be used by TSSLSocket. // // It's nil-safe. If tc is nil, nil will be returned instead. func (tc *TConfiguration) GetTLSConfig() *tls.Config { if tc == nil { return nil } return tc.TLSConfig } // GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol // should follow. // // It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or // tc.TBinaryStrictRead is nil. func (tc *TConfiguration) GetTBinaryStrictRead() bool { if tc == nil || tc.TBinaryStrictRead == nil { return DEFAULT_TBINARY_STRICT_READ } return *tc.TBinaryStrictRead } // GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol // should follow. // // It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or // tc.TBinaryStrictWrite is nil. func (tc *TConfiguration) GetTBinaryStrictWrite() bool { if tc == nil || tc.TBinaryStrictWrite == nil { return DEFAULT_TBINARY_STRICT_WRITE } return *tc.TBinaryStrictWrite } // GetTHeaderProtocolID returns the THeaderProtocolID should be used by // THeaderProtocol clients (for servers, they always use the same one as the // client instead). // // It's nil-safe. If either tc or tc.THeaderProtocolID is nil, // THeaderProtocolDefault will be returned instead. // THeaderProtocolDefault will also be returned if configured value is invalid. func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID { if tc == nil || tc.THeaderProtocolID == nil { return THeaderProtocolDefault } protoID := *tc.THeaderProtocolID if err := protoID.Validate(); err != nil { return THeaderProtocolDefault } return protoID } // THeaderProtocolIDPtr validates and returns the pointer to id. // // If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault // and the validation error will be returned. func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) { err := id.Validate() if err != nil { id = THeaderProtocolDefault } return &id, err } // THeaderProtocolIDPtrMust validates and returns the pointer to id. // // It's similar to THeaderProtocolIDPtr, but it panics on validation errors // instead of returning them. func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID { ptr, err := THeaderProtocolIDPtr(id) if err != nil { panic(err) } return ptr } // TConfigurationSetter is an optional interface TProtocol, TTransport, // TProtocolFactory, TTransportFactory, and other implementations can implement. // // It's intended to be called during intializations. // The behavior of calling SetTConfiguration on a TTransport/TProtocol in the // middle of a message is undefined: // It may or may not change the behavior of the current processing message, // and it may even cause the current message to fail. // // Note for implementations: SetTConfiguration might be called multiple times // with the same value in quick successions due to the implementation of the // propagation. Implementations should make SetTConfiguration as simple as // possible (usually just overwrite the stored configuration and propagate it to // the wrapped TTransports/TProtocols). type TConfigurationSetter interface { SetTConfiguration(*TConfiguration) } // PropagateTConfiguration propagates cfg to impl if impl implements // TConfigurationSetter and cfg is non-nil, otherwise it does nothing. // // NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration // with everything being default value, use &TConfiguration{} explicitly instead. func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) { if cfg == nil || cfg.noPropagation { return } if setter, ok := impl.(TConfigurationSetter); ok { setter.SetTConfiguration(cfg) } } func checkSizeForProtocol(size int32, cfg *TConfiguration) error { if size < 0 { return NewTProtocolExceptionWithType( NEGATIVE_SIZE, fmt.Errorf("negative size: %d", size), ) } if size > cfg.GetMaxMessageSize() { return NewTProtocolExceptionWithType( SIZE_LIMIT, fmt.Errorf("size exceeded max allowed: %d", size), ) } return nil } type tTransportFactoryConf struct { delegate TTransportFactory cfg *TConfiguration } func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) { trans, err := f.delegate.GetTransport(orig) if err == nil { PropagateTConfiguration(orig, f.cfg) PropagateTConfiguration(trans, f.cfg) } return trans, err } func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(f.delegate, f.cfg) f.cfg = cfg } // TTransportFactoryConf wraps a TTransportFactory to propagate // TConfiguration on the factory's GetTransport calls. func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory { return &tTransportFactoryConf{ delegate: delegate, cfg: conf, } } type tProtocolFactoryConf struct { delegate TProtocolFactory cfg *TConfiguration } func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol { proto := f.delegate.GetProtocol(trans) PropagateTConfiguration(trans, f.cfg) PropagateTConfiguration(proto, f.cfg) return proto } func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(f.delegate, f.cfg) f.cfg = cfg } // TProtocolFactoryConf wraps a TProtocolFactory to propagate // TConfiguration on the factory's GetProtocol calls. func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory { return &tProtocolFactoryConf{ delegate: delegate, cfg: conf, } } var ( _ TConfigurationSetter = (*tTransportFactoryConf)(nil) _ TConfigurationSetter = (*tProtocolFactoryConf)(nil) ) context.go000066400000000000000000000015551414226744000341710ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "context" var defaultCtx = context.Background() debug_protocol.go000066400000000000000000000371721414226744000355200ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "fmt" ) type TDebugProtocol struct { // Required. The actual TProtocol to do the read/write. Delegate TProtocol // Optional. The logger and prefix to log all the args/return values // from Delegate TProtocol calls. // // If Logger is nil, StdLogger using stdlib log package with os.Stderr // will be used. If disable logging is desired, set Logger to NopLogger // explicitly instead of leaving it as nil/unset. Logger Logger LogPrefix string // Optional. An TProtocol to duplicate everything read/written from Delegate. // // A typical use case of this is to use TSimpleJSONProtocol wrapping // TMemoryBuffer in a middleware to json logging requests/responses. // // This feature is not available from TDebugProtocolFactory. In order to // use it you have to construct TDebugProtocol directly, or set DuplicateTo // field after getting a TDebugProtocol from the factory. DuplicateTo TProtocol } type TDebugProtocolFactory struct { Underlying TProtocolFactory LogPrefix string Logger Logger } // NewTDebugProtocolFactory creates a TDebugProtocolFactory. // // Deprecated: Please use NewTDebugProtocolFactoryWithLogger or the struct // itself instead. This version will use the default logger from standard // library. func NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory { return &TDebugProtocolFactory{ Underlying: underlying, LogPrefix: logPrefix, Logger: StdLogger(nil), } } // NewTDebugProtocolFactoryWithLogger creates a TDebugProtocolFactory. func NewTDebugProtocolFactoryWithLogger(underlying TProtocolFactory, logPrefix string, logger Logger) *TDebugProtocolFactory { return &TDebugProtocolFactory{ Underlying: underlying, LogPrefix: logPrefix, Logger: logger, } } func (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol { return &TDebugProtocol{ Delegate: t.Underlying.GetProtocol(trans), LogPrefix: t.LogPrefix, Logger: fallbackLogger(t.Logger), } } func (tdp *TDebugProtocol) logf(format string, v ...interface{}) { fallbackLogger(tdp.Logger)(fmt.Sprintf(format, v...)) } func (tdp *TDebugProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { err := tdp.Delegate.WriteMessageBegin(ctx, name, typeId, seqid) tdp.logf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid) } return err } func (tdp *TDebugProtocol) WriteMessageEnd(ctx context.Context) error { err := tdp.Delegate.WriteMessageEnd(ctx) tdp.logf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMessageEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteStructBegin(ctx context.Context, name string) error { err := tdp.Delegate.WriteStructBegin(ctx, name) tdp.logf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteStructBegin(ctx, name) } return err } func (tdp *TDebugProtocol) WriteStructEnd(ctx context.Context) error { err := tdp.Delegate.WriteStructEnd(ctx) tdp.logf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteStructEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { err := tdp.Delegate.WriteFieldBegin(ctx, name, typeId, id) tdp.logf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id) } return err } func (tdp *TDebugProtocol) WriteFieldEnd(ctx context.Context) error { err := tdp.Delegate.WriteFieldEnd(ctx) tdp.logf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteFieldEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteFieldStop(ctx context.Context) error { err := tdp.Delegate.WriteFieldStop(ctx) tdp.logf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteFieldStop(ctx) } return err } func (tdp *TDebugProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { err := tdp.Delegate.WriteMapBegin(ctx, keyType, valueType, size) tdp.logf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size) } return err } func (tdp *TDebugProtocol) WriteMapEnd(ctx context.Context) error { err := tdp.Delegate.WriteMapEnd(ctx) tdp.logf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMapEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { err := tdp.Delegate.WriteListBegin(ctx, elemType, size) tdp.logf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteListBegin(ctx, elemType, size) } return err } func (tdp *TDebugProtocol) WriteListEnd(ctx context.Context) error { err := tdp.Delegate.WriteListEnd(ctx) tdp.logf("%sWriteListEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteListEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { err := tdp.Delegate.WriteSetBegin(ctx, elemType, size) tdp.logf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size) } return err } func (tdp *TDebugProtocol) WriteSetEnd(ctx context.Context) error { err := tdp.Delegate.WriteSetEnd(ctx) tdp.logf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteSetEnd(ctx) } return err } func (tdp *TDebugProtocol) WriteBool(ctx context.Context, value bool) error { err := tdp.Delegate.WriteBool(ctx, value) tdp.logf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteBool(ctx, value) } return err } func (tdp *TDebugProtocol) WriteByte(ctx context.Context, value int8) error { err := tdp.Delegate.WriteByte(ctx, value) tdp.logf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteByte(ctx, value) } return err } func (tdp *TDebugProtocol) WriteI16(ctx context.Context, value int16) error { err := tdp.Delegate.WriteI16(ctx, value) tdp.logf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI16(ctx, value) } return err } func (tdp *TDebugProtocol) WriteI32(ctx context.Context, value int32) error { err := tdp.Delegate.WriteI32(ctx, value) tdp.logf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI32(ctx, value) } return err } func (tdp *TDebugProtocol) WriteI64(ctx context.Context, value int64) error { err := tdp.Delegate.WriteI64(ctx, value) tdp.logf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI64(ctx, value) } return err } func (tdp *TDebugProtocol) WriteDouble(ctx context.Context, value float64) error { err := tdp.Delegate.WriteDouble(ctx, value) tdp.logf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteDouble(ctx, value) } return err } func (tdp *TDebugProtocol) WriteString(ctx context.Context, value string) error { err := tdp.Delegate.WriteString(ctx, value) tdp.logf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteString(ctx, value) } return err } func (tdp *TDebugProtocol) WriteBinary(ctx context.Context, value []byte) error { err := tdp.Delegate.WriteBinary(ctx, value) tdp.logf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteBinary(ctx, value) } return err } func (tdp *TDebugProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) { name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin(ctx) tdp.logf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid) } return } func (tdp *TDebugProtocol) ReadMessageEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadMessageEnd(ctx) tdp.logf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMessageEnd(ctx) } return } func (tdp *TDebugProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { name, err = tdp.Delegate.ReadStructBegin(ctx) tdp.logf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteStructBegin(ctx, name) } return } func (tdp *TDebugProtocol) ReadStructEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadStructEnd(ctx) tdp.logf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteStructEnd(ctx) } return } func (tdp *TDebugProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) { name, typeId, id, err = tdp.Delegate.ReadFieldBegin(ctx) tdp.logf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id) } return } func (tdp *TDebugProtocol) ReadFieldEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadFieldEnd(ctx) tdp.logf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteFieldEnd(ctx) } return } func (tdp *TDebugProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { keyType, valueType, size, err = tdp.Delegate.ReadMapBegin(ctx) tdp.logf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size) } return } func (tdp *TDebugProtocol) ReadMapEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadMapEnd(ctx) tdp.logf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteMapEnd(ctx) } return } func (tdp *TDebugProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { elemType, size, err = tdp.Delegate.ReadListBegin(ctx) tdp.logf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteListBegin(ctx, elemType, size) } return } func (tdp *TDebugProtocol) ReadListEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadListEnd(ctx) tdp.logf("%sReadListEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteListEnd(ctx) } return } func (tdp *TDebugProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { elemType, size, err = tdp.Delegate.ReadSetBegin(ctx) tdp.logf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size) } return } func (tdp *TDebugProtocol) ReadSetEnd(ctx context.Context) (err error) { err = tdp.Delegate.ReadSetEnd(ctx) tdp.logf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteSetEnd(ctx) } return } func (tdp *TDebugProtocol) ReadBool(ctx context.Context) (value bool, err error) { value, err = tdp.Delegate.ReadBool(ctx) tdp.logf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteBool(ctx, value) } return } func (tdp *TDebugProtocol) ReadByte(ctx context.Context) (value int8, err error) { value, err = tdp.Delegate.ReadByte(ctx) tdp.logf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteByte(ctx, value) } return } func (tdp *TDebugProtocol) ReadI16(ctx context.Context) (value int16, err error) { value, err = tdp.Delegate.ReadI16(ctx) tdp.logf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI16(ctx, value) } return } func (tdp *TDebugProtocol) ReadI32(ctx context.Context) (value int32, err error) { value, err = tdp.Delegate.ReadI32(ctx) tdp.logf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI32(ctx, value) } return } func (tdp *TDebugProtocol) ReadI64(ctx context.Context) (value int64, err error) { value, err = tdp.Delegate.ReadI64(ctx) tdp.logf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteI64(ctx, value) } return } func (tdp *TDebugProtocol) ReadDouble(ctx context.Context) (value float64, err error) { value, err = tdp.Delegate.ReadDouble(ctx) tdp.logf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteDouble(ctx, value) } return } func (tdp *TDebugProtocol) ReadString(ctx context.Context) (value string, err error) { value, err = tdp.Delegate.ReadString(ctx) tdp.logf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteString(ctx, value) } return } func (tdp *TDebugProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { value, err = tdp.Delegate.ReadBinary(ctx) tdp.logf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.WriteBinary(ctx, value) } return } func (tdp *TDebugProtocol) Skip(ctx context.Context, fieldType TType) (err error) { err = tdp.Delegate.Skip(ctx, fieldType) tdp.logf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.Skip(ctx, fieldType) } return } func (tdp *TDebugProtocol) Flush(ctx context.Context) (err error) { err = tdp.Delegate.Flush(ctx) tdp.logf("%sFlush() (err=%#v)", tdp.LogPrefix, err) if tdp.DuplicateTo != nil { tdp.DuplicateTo.Flush(ctx) } return } func (tdp *TDebugProtocol) Transport() TTransport { return tdp.Delegate.Transport() } // SetTConfiguration implements TConfigurationSetter for propagation. func (tdp *TDebugProtocol) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(tdp.Delegate, conf) PropagateTConfiguration(tdp.DuplicateTo, conf) } var _ TConfigurationSetter = (*TDebugProtocol)(nil) deserializer.go000066400000000000000000000061741414226744000351710ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "sync" ) type TDeserializer struct { Transport *TMemoryBuffer Protocol TProtocol } func NewTDeserializer() *TDeserializer { transport := NewTMemoryBufferLen(1024) protocol := NewTBinaryProtocolTransport(transport) return &TDeserializer{ Transport: transport, Protocol: protocol, } } func (t *TDeserializer) ReadString(ctx context.Context, msg TStruct, s string) (err error) { t.Transport.Reset() err = nil if _, err = t.Transport.Write([]byte(s)); err != nil { return } if err = msg.Read(ctx, t.Protocol); err != nil { return } return } func (t *TDeserializer) Read(ctx context.Context, msg TStruct, b []byte) (err error) { t.Transport.Reset() err = nil if _, err = t.Transport.Write(b); err != nil { return } if err = msg.Read(ctx, t.Protocol); err != nil { return } return } // TDeserializerPool is the thread-safe version of TDeserializer, // it uses resource pool of TDeserializer under the hood. // // It must be initialized with either NewTDeserializerPool or // NewTDeserializerPoolSizeFactory. type TDeserializerPool struct { pool sync.Pool } // NewTDeserializerPool creates a new TDeserializerPool. // // NewTDeserializer can be used as the arg here. func NewTDeserializerPool(f func() *TDeserializer) *TDeserializerPool { return &TDeserializerPool{ pool: sync.Pool{ New: func() interface{} { return f() }, }, } } // NewTDeserializerPoolSizeFactory creates a new TDeserializerPool with // the given size and protocol factory. // // Note that the size is not the limit. The TMemoryBuffer underneath can grow // larger than that. It just dictates the initial size. func NewTDeserializerPoolSizeFactory(size int, factory TProtocolFactory) *TDeserializerPool { return &TDeserializerPool{ pool: sync.Pool{ New: func() interface{} { transport := NewTMemoryBufferLen(size) protocol := factory.GetProtocol(transport) return &TDeserializer{ Transport: transport, Protocol: protocol, } }, }, } } func (t *TDeserializerPool) ReadString(ctx context.Context, msg TStruct, s string) error { d := t.pool.Get().(*TDeserializer) defer t.pool.Put(d) return d.ReadString(ctx, msg, s) } func (t *TDeserializerPool) Read(ctx context.Context, msg TStruct, b []byte) error { d := t.pool.Get().(*TDeserializer) defer t.pool.Put(d) return d.Read(ctx, msg, b) } exception.go000066400000000000000000000061301414226744000344750ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" ) // Generic Thrift exception type TException interface { error TExceptionType() TExceptionType } // Prepends additional information to an error without losing the Thrift exception interface func PrependError(prepend string, err error) error { msg := prepend + err.Error() var te TException if errors.As(err, &te) { switch te.TExceptionType() { case TExceptionTypeTransport: if t, ok := err.(TTransportException); ok { return prependTTransportException(prepend, t) } case TExceptionTypeProtocol: if t, ok := err.(TProtocolException); ok { return prependTProtocolException(prepend, t) } case TExceptionTypeApplication: var t TApplicationException if errors.As(err, &t) { return NewTApplicationException(t.TypeId(), msg) } } return wrappedTException{ err: err, msg: msg, tExceptionType: te.TExceptionType(), } } return errors.New(msg) } // TExceptionType is an enum type to categorize different "subclasses" of TExceptions. type TExceptionType byte // TExceptionType values const ( TExceptionTypeUnknown TExceptionType = iota TExceptionTypeCompiled // TExceptions defined in thrift files and generated by thrift compiler TExceptionTypeApplication // TApplicationExceptions TExceptionTypeProtocol // TProtocolExceptions TExceptionTypeTransport // TTransportExceptions ) // WrapTException wraps an error into TException. // // If err is nil or already TException, it's returned as-is. // Otherwise it will be wraped into TException with TExceptionType() returning // TExceptionTypeUnknown, and Unwrap() returning the original error. func WrapTException(err error) TException { if err == nil { return nil } if te, ok := err.(TException); ok { return te } return wrappedTException{ err: err, msg: err.Error(), tExceptionType: TExceptionTypeUnknown, } } type wrappedTException struct { err error msg string tExceptionType TExceptionType } func (w wrappedTException) Error() string { return w.msg } func (w wrappedTException) TExceptionType() TExceptionType { return w.tExceptionType } func (w wrappedTException) Unwrap() error { return w.err } var _ TException = wrappedTException{} framed_transport.go000066400000000000000000000135061414226744000360560ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "bytes" "context" "encoding/binary" "fmt" "io" ) // Deprecated: Use DEFAULT_MAX_FRAME_SIZE instead. const DEFAULT_MAX_LENGTH = 16384000 type TFramedTransport struct { transport TTransport cfg *TConfiguration writeBuf bytes.Buffer reader *bufio.Reader readBuf bytes.Buffer buffer [4]byte } type tFramedTransportFactory struct { factory TTransportFactory cfg *TConfiguration } // Deprecated: Use NewTFramedTransportFactoryConf instead. func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory { return NewTFramedTransportFactoryConf(factory, &TConfiguration{ MaxFrameSize: DEFAULT_MAX_LENGTH, noPropagation: true, }) } // Deprecated: Use NewTFramedTransportFactoryConf instead. func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory { return NewTFramedTransportFactoryConf(factory, &TConfiguration{ MaxFrameSize: int32(maxLength), noPropagation: true, }) } func NewTFramedTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory { PropagateTConfiguration(factory, conf) return &tFramedTransportFactory{ factory: factory, cfg: conf, } } func (p *tFramedTransportFactory) GetTransport(base TTransport) (TTransport, error) { PropagateTConfiguration(base, p.cfg) tt, err := p.factory.GetTransport(base) if err != nil { return nil, err } return NewTFramedTransportConf(tt, p.cfg), nil } func (p *tFramedTransportFactory) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(p.factory, cfg) p.cfg = cfg } // Deprecated: Use NewTFramedTransportConf instead. func NewTFramedTransport(transport TTransport) *TFramedTransport { return NewTFramedTransportConf(transport, &TConfiguration{ MaxFrameSize: DEFAULT_MAX_LENGTH, noPropagation: true, }) } // Deprecated: Use NewTFramedTransportConf instead. func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport { return NewTFramedTransportConf(transport, &TConfiguration{ MaxFrameSize: int32(maxLength), noPropagation: true, }) } func NewTFramedTransportConf(transport TTransport, conf *TConfiguration) *TFramedTransport { PropagateTConfiguration(transport, conf) return &TFramedTransport{ transport: transport, reader: bufio.NewReader(transport), cfg: conf, } } func (p *TFramedTransport) Open() error { return p.transport.Open() } func (p *TFramedTransport) IsOpen() bool { return p.transport.IsOpen() } func (p *TFramedTransport) Close() error { return p.transport.Close() } func (p *TFramedTransport) Read(buf []byte) (read int, err error) { read, err = p.readBuf.Read(buf) if err != io.EOF { return } // For bytes.Buffer.Read, EOF would only happen when read is zero, // but still, do a sanity check, // in case that behavior is changed in a future version of go stdlib. // When that happens, just return nil error, // and let the caller call Read again to read the next frame. if read > 0 { return read, nil } // Reaching here means that the last Read finished the last frame, // so we need to read the next frame into readBuf now. if err = p.readFrame(); err != nil { return read, err } newRead, err := p.Read(buf[read:]) return read + newRead, err } func (p *TFramedTransport) ReadByte() (c byte, err error) { buf := p.buffer[:1] _, err = p.Read(buf) if err != nil { return } c = buf[0] return } func (p *TFramedTransport) Write(buf []byte) (int, error) { n, err := p.writeBuf.Write(buf) return n, NewTTransportExceptionFromError(err) } func (p *TFramedTransport) WriteByte(c byte) error { return p.writeBuf.WriteByte(c) } func (p *TFramedTransport) WriteString(s string) (n int, err error) { return p.writeBuf.WriteString(s) } func (p *TFramedTransport) Flush(ctx context.Context) error { size := p.writeBuf.Len() buf := p.buffer[:4] binary.BigEndian.PutUint32(buf, uint32(size)) _, err := p.transport.Write(buf) if err != nil { p.writeBuf.Reset() return NewTTransportExceptionFromError(err) } if size > 0 { if _, err := io.Copy(p.transport, &p.writeBuf); err != nil { p.writeBuf.Reset() return NewTTransportExceptionFromError(err) } } err = p.transport.Flush(ctx) return NewTTransportExceptionFromError(err) } func (p *TFramedTransport) readFrame() error { buf := p.buffer[:4] if _, err := io.ReadFull(p.reader, buf); err != nil { return err } size := binary.BigEndian.Uint32(buf) if size < 0 || size > uint32(p.cfg.GetMaxFrameSize()) { return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size)) } _, err := io.CopyN(&p.readBuf, p.reader, int64(size)) return NewTTransportExceptionFromError(err) } func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) { return uint64(p.readBuf.Len()) } // SetTConfiguration implements TConfigurationSetter. func (p *TFramedTransport) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(p.transport, cfg) p.cfg = cfg } var ( _ TConfigurationSetter = (*tFramedTransportFactory)(nil) _ TConfigurationSetter = (*TFramedTransport)(nil) ) header_context.go000066400000000000000000000056561414226744000355070ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" ) // See https://godoc.org/context#WithValue on why do we need the unexported typedefs. type ( headerKey string headerKeyList int ) // Values for headerKeyList. const ( headerKeyListRead headerKeyList = iota headerKeyListWrite ) // SetHeader sets a header in the context. func SetHeader(ctx context.Context, key, value string) context.Context { return context.WithValue( ctx, headerKey(key), value, ) } // UnsetHeader unsets a previously set header in the context. func UnsetHeader(ctx context.Context, key string) context.Context { return context.WithValue( ctx, headerKey(key), nil, ) } // GetHeader returns a value of the given header from the context. func GetHeader(ctx context.Context, key string) (value string, ok bool) { if v := ctx.Value(headerKey(key)); v != nil { value, ok = v.(string) } return } // SetReadHeaderList sets the key list of read THeaders in the context. func SetReadHeaderList(ctx context.Context, keys []string) context.Context { return context.WithValue( ctx, headerKeyListRead, keys, ) } // GetReadHeaderList returns the key list of read THeaders from the context. func GetReadHeaderList(ctx context.Context) []string { if v := ctx.Value(headerKeyListRead); v != nil { if value, ok := v.([]string); ok { return value } } return nil } // SetWriteHeaderList sets the key list of THeaders to write in the context. func SetWriteHeaderList(ctx context.Context, keys []string) context.Context { return context.WithValue( ctx, headerKeyListWrite, keys, ) } // GetWriteHeaderList returns the key list of THeaders to write from the context. func GetWriteHeaderList(ctx context.Context) []string { if v := ctx.Value(headerKeyListWrite); v != nil { if value, ok := v.([]string); ok { return value } } return nil } // AddReadTHeaderToContext adds the whole THeader headers into context. func AddReadTHeaderToContext(ctx context.Context, headers THeaderMap) context.Context { keys := make([]string, 0, len(headers)) for key, value := range headers { ctx = SetHeader(ctx, key, value) keys = append(keys, key) } return SetReadHeaderList(ctx, keys) } header_protocol.go000066400000000000000000000246261414226744000356620ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "errors" ) // THeaderProtocol is a thrift protocol that implements THeader: // https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md // // It supports either binary or compact protocol as the wrapped protocol. // // Most of the THeader handlings are happening inside THeaderTransport. type THeaderProtocol struct { transport *THeaderTransport // Will be initialized on first read/write. protocol TProtocol cfg *TConfiguration } // Deprecated: Use NewTHeaderProtocolConf instead. func NewTHeaderProtocol(trans TTransport) *THeaderProtocol { return newTHeaderProtocolConf(trans, &TConfiguration{ noPropagation: true, }) } // NewTHeaderProtocolConf creates a new THeaderProtocol from the underlying // transport with given TConfiguration. // // The passed in transport will be wrapped with THeaderTransport. // // Note that THeaderTransport handles frame and zlib by itself, // so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), // instead of rich transports like TZlibTransport or TFramedTransport. func NewTHeaderProtocolConf(trans TTransport, conf *TConfiguration) *THeaderProtocol { return newTHeaderProtocolConf(trans, conf) } func newTHeaderProtocolConf(trans TTransport, cfg *TConfiguration) *THeaderProtocol { t := NewTHeaderTransportConf(trans, cfg) p, _ := t.cfg.GetTHeaderProtocolID().GetProtocol(t) PropagateTConfiguration(p, cfg) return &THeaderProtocol{ transport: t, protocol: p, cfg: cfg, } } type tHeaderProtocolFactory struct { cfg *TConfiguration } func (f tHeaderProtocolFactory) GetProtocol(trans TTransport) TProtocol { return newTHeaderProtocolConf(trans, f.cfg) } func (f *tHeaderProtocolFactory) SetTConfiguration(cfg *TConfiguration) { f.cfg = cfg } // Deprecated: Use NewTHeaderProtocolFactoryConf instead. func NewTHeaderProtocolFactory() TProtocolFactory { return NewTHeaderProtocolFactoryConf(&TConfiguration{ noPropagation: true, }) } // NewTHeaderProtocolFactoryConf creates a factory for THeader with given // TConfiguration. func NewTHeaderProtocolFactoryConf(conf *TConfiguration) TProtocolFactory { return tHeaderProtocolFactory{ cfg: conf, } } // Transport returns the underlying transport. // // It's guaranteed to be of type *THeaderTransport. func (p *THeaderProtocol) Transport() TTransport { return p.transport } // GetReadHeaders returns the THeaderMap read from transport. func (p *THeaderProtocol) GetReadHeaders() THeaderMap { return p.transport.GetReadHeaders() } // SetWriteHeader sets a header for write. func (p *THeaderProtocol) SetWriteHeader(key, value string) { p.transport.SetWriteHeader(key, value) } // ClearWriteHeaders clears all write headers previously set. func (p *THeaderProtocol) ClearWriteHeaders() { p.transport.ClearWriteHeaders() } // AddTransform add a transform for writing. func (p *THeaderProtocol) AddTransform(transform THeaderTransformID) error { return p.transport.AddTransform(transform) } func (p *THeaderProtocol) Flush(ctx context.Context) error { return p.transport.Flush(ctx) } func (p *THeaderProtocol) WriteMessageBegin(ctx context.Context, name string, typeID TMessageType, seqID int32) error { newProto, err := p.transport.Protocol().GetProtocol(p.transport) if err != nil { return err } PropagateTConfiguration(newProto, p.cfg) p.protocol = newProto p.transport.SequenceID = seqID return p.protocol.WriteMessageBegin(ctx, name, typeID, seqID) } func (p *THeaderProtocol) WriteMessageEnd(ctx context.Context) error { if err := p.protocol.WriteMessageEnd(ctx); err != nil { return err } return p.transport.Flush(ctx) } func (p *THeaderProtocol) WriteStructBegin(ctx context.Context, name string) error { return p.protocol.WriteStructBegin(ctx, name) } func (p *THeaderProtocol) WriteStructEnd(ctx context.Context) error { return p.protocol.WriteStructEnd(ctx) } func (p *THeaderProtocol) WriteFieldBegin(ctx context.Context, name string, typeID TType, id int16) error { return p.protocol.WriteFieldBegin(ctx, name, typeID, id) } func (p *THeaderProtocol) WriteFieldEnd(ctx context.Context) error { return p.protocol.WriteFieldEnd(ctx) } func (p *THeaderProtocol) WriteFieldStop(ctx context.Context) error { return p.protocol.WriteFieldStop(ctx) } func (p *THeaderProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { return p.protocol.WriteMapBegin(ctx, keyType, valueType, size) } func (p *THeaderProtocol) WriteMapEnd(ctx context.Context) error { return p.protocol.WriteMapEnd(ctx) } func (p *THeaderProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.protocol.WriteListBegin(ctx, elemType, size) } func (p *THeaderProtocol) WriteListEnd(ctx context.Context) error { return p.protocol.WriteListEnd(ctx) } func (p *THeaderProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.protocol.WriteSetBegin(ctx, elemType, size) } func (p *THeaderProtocol) WriteSetEnd(ctx context.Context) error { return p.protocol.WriteSetEnd(ctx) } func (p *THeaderProtocol) WriteBool(ctx context.Context, value bool) error { return p.protocol.WriteBool(ctx, value) } func (p *THeaderProtocol) WriteByte(ctx context.Context, value int8) error { return p.protocol.WriteByte(ctx, value) } func (p *THeaderProtocol) WriteI16(ctx context.Context, value int16) error { return p.protocol.WriteI16(ctx, value) } func (p *THeaderProtocol) WriteI32(ctx context.Context, value int32) error { return p.protocol.WriteI32(ctx, value) } func (p *THeaderProtocol) WriteI64(ctx context.Context, value int64) error { return p.protocol.WriteI64(ctx, value) } func (p *THeaderProtocol) WriteDouble(ctx context.Context, value float64) error { return p.protocol.WriteDouble(ctx, value) } func (p *THeaderProtocol) WriteString(ctx context.Context, value string) error { return p.protocol.WriteString(ctx, value) } func (p *THeaderProtocol) WriteBinary(ctx context.Context, value []byte) error { return p.protocol.WriteBinary(ctx, value) } // ReadFrame calls underlying THeaderTransport's ReadFrame function. func (p *THeaderProtocol) ReadFrame(ctx context.Context) error { return p.transport.ReadFrame(ctx) } func (p *THeaderProtocol) ReadMessageBegin(ctx context.Context) (name string, typeID TMessageType, seqID int32, err error) { if err = p.transport.ReadFrame(ctx); err != nil { return } var newProto TProtocol newProto, err = p.transport.Protocol().GetProtocol(p.transport) if err != nil { var tAppExc TApplicationException if !errors.As(err, &tAppExc) { return } if e := p.protocol.WriteMessageBegin(ctx, "", EXCEPTION, seqID); e != nil { return } if e := tAppExc.Write(ctx, p.protocol); e != nil { return } if e := p.protocol.WriteMessageEnd(ctx); e != nil { return } if e := p.transport.Flush(ctx); e != nil { return } return } PropagateTConfiguration(newProto, p.cfg) p.protocol = newProto return p.protocol.ReadMessageBegin(ctx) } func (p *THeaderProtocol) ReadMessageEnd(ctx context.Context) error { return p.protocol.ReadMessageEnd(ctx) } func (p *THeaderProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { return p.protocol.ReadStructBegin(ctx) } func (p *THeaderProtocol) ReadStructEnd(ctx context.Context) error { return p.protocol.ReadStructEnd(ctx) } func (p *THeaderProtocol) ReadFieldBegin(ctx context.Context) (name string, typeID TType, id int16, err error) { return p.protocol.ReadFieldBegin(ctx) } func (p *THeaderProtocol) ReadFieldEnd(ctx context.Context) error { return p.protocol.ReadFieldEnd(ctx) } func (p *THeaderProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { return p.protocol.ReadMapBegin(ctx) } func (p *THeaderProtocol) ReadMapEnd(ctx context.Context) error { return p.protocol.ReadMapEnd(ctx) } func (p *THeaderProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { return p.protocol.ReadListBegin(ctx) } func (p *THeaderProtocol) ReadListEnd(ctx context.Context) error { return p.protocol.ReadListEnd(ctx) } func (p *THeaderProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { return p.protocol.ReadSetBegin(ctx) } func (p *THeaderProtocol) ReadSetEnd(ctx context.Context) error { return p.protocol.ReadSetEnd(ctx) } func (p *THeaderProtocol) ReadBool(ctx context.Context) (value bool, err error) { return p.protocol.ReadBool(ctx) } func (p *THeaderProtocol) ReadByte(ctx context.Context) (value int8, err error) { return p.protocol.ReadByte(ctx) } func (p *THeaderProtocol) ReadI16(ctx context.Context) (value int16, err error) { return p.protocol.ReadI16(ctx) } func (p *THeaderProtocol) ReadI32(ctx context.Context) (value int32, err error) { return p.protocol.ReadI32(ctx) } func (p *THeaderProtocol) ReadI64(ctx context.Context) (value int64, err error) { return p.protocol.ReadI64(ctx) } func (p *THeaderProtocol) ReadDouble(ctx context.Context) (value float64, err error) { return p.protocol.ReadDouble(ctx) } func (p *THeaderProtocol) ReadString(ctx context.Context) (value string, err error) { return p.protocol.ReadString(ctx) } func (p *THeaderProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { return p.protocol.ReadBinary(ctx) } func (p *THeaderProtocol) Skip(ctx context.Context, fieldType TType) error { return p.protocol.Skip(ctx, fieldType) } // SetTConfiguration implements TConfigurationSetter. func (p *THeaderProtocol) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(p.transport, cfg) PropagateTConfiguration(p.protocol, cfg) p.cfg = cfg } var ( _ TConfigurationSetter = (*tHeaderProtocolFactory)(nil) _ TConfigurationSetter = (*THeaderProtocol)(nil) ) header_transport.go000066400000000000000000000540641414226744000360540ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "bytes" "compress/zlib" "context" "encoding/binary" "errors" "fmt" "io" "io/ioutil" ) // Size in bytes for 32-bit ints. const size32 = 4 type headerMeta struct { MagicFlags uint32 SequenceID int32 HeaderLength uint16 } const headerMetaSize = 10 type clientType int const ( clientUnknown clientType = iota clientHeaders clientFramedBinary clientUnframedBinary clientFramedCompact clientUnframedCompact ) // Constants defined in THeader format: // https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md const ( THeaderHeaderMagic uint32 = 0x0fff0000 THeaderHeaderMask uint32 = 0xffff0000 THeaderFlagsMask uint32 = 0x0000ffff THeaderMaxFrameSize uint32 = 0x3fffffff ) // THeaderMap is the type of the header map in THeader transport. type THeaderMap map[string]string // THeaderProtocolID is the wrapped protocol id used in THeader. type THeaderProtocolID int32 // Supported THeaderProtocolID values. const ( THeaderProtocolBinary THeaderProtocolID = 0x00 THeaderProtocolCompact THeaderProtocolID = 0x02 THeaderProtocolDefault = THeaderProtocolBinary ) // Declared globally to avoid repetitive allocations, not really used. var globalMemoryBuffer = NewTMemoryBuffer() // Validate checks whether the THeaderProtocolID is a valid/supported one. func (id THeaderProtocolID) Validate() error { _, err := id.GetProtocol(globalMemoryBuffer) return err } // GetProtocol gets the corresponding TProtocol from the wrapped protocol id. func (id THeaderProtocolID) GetProtocol(trans TTransport) (TProtocol, error) { switch id { default: return nil, NewTApplicationException( INVALID_PROTOCOL, fmt.Sprintf("THeader protocol id %d not supported", id), ) case THeaderProtocolBinary: return NewTBinaryProtocolTransport(trans), nil case THeaderProtocolCompact: return NewTCompactProtocol(trans), nil } } // THeaderTransformID defines the numeric id of the transform used. type THeaderTransformID int32 // THeaderTransformID values. // // Values not defined here are not currently supported, namely HMAC and Snappy. const ( TransformNone THeaderTransformID = iota // 0, no special handling TransformZlib // 1, zlib ) var supportedTransformIDs = map[THeaderTransformID]bool{ TransformNone: true, TransformZlib: true, } // TransformReader is an io.ReadCloser that handles transforms reading. type TransformReader struct { io.Reader closers []io.Closer } var _ io.ReadCloser = (*TransformReader)(nil) // NewTransformReaderWithCapacity initializes a TransformReader with expected // closers capacity. // // If you don't know the closers capacity beforehand, just use // // &TransformReader{Reader: baseReader} // // instead would be sufficient. func NewTransformReaderWithCapacity(baseReader io.Reader, capacity int) *TransformReader { return &TransformReader{ Reader: baseReader, closers: make([]io.Closer, 0, capacity), } } // Close calls the underlying closers in appropriate order, // stops at and returns the first error encountered. func (tr *TransformReader) Close() error { // Call closers in reversed order for i := len(tr.closers) - 1; i >= 0; i-- { if err := tr.closers[i].Close(); err != nil { return err } } return nil } // AddTransform adds a transform. func (tr *TransformReader) AddTransform(id THeaderTransformID) error { switch id { default: return NewTApplicationException( INVALID_TRANSFORM, fmt.Sprintf("THeaderTransformID %d not supported", id), ) case TransformNone: // no-op case TransformZlib: readCloser, err := zlib.NewReader(tr.Reader) if err != nil { return err } tr.Reader = readCloser tr.closers = append(tr.closers, readCloser) } return nil } // TransformWriter is an io.WriteCloser that handles transforms writing. type TransformWriter struct { io.Writer closers []io.Closer } var _ io.WriteCloser = (*TransformWriter)(nil) // NewTransformWriter creates a new TransformWriter with base writer and transforms. func NewTransformWriter(baseWriter io.Writer, transforms []THeaderTransformID) (io.WriteCloser, error) { writer := &TransformWriter{ Writer: baseWriter, closers: make([]io.Closer, 0, len(transforms)), } for _, id := range transforms { if err := writer.AddTransform(id); err != nil { return nil, err } } return writer, nil } // Close calls the underlying closers in appropriate order, // stops at and returns the first error encountered. func (tw *TransformWriter) Close() error { // Call closers in reversed order for i := len(tw.closers) - 1; i >= 0; i-- { if err := tw.closers[i].Close(); err != nil { return err } } return nil } // AddTransform adds a transform. func (tw *TransformWriter) AddTransform(id THeaderTransformID) error { switch id { default: return NewTApplicationException( INVALID_TRANSFORM, fmt.Sprintf("THeaderTransformID %d not supported", id), ) case TransformNone: // no-op case TransformZlib: writeCloser := zlib.NewWriter(tw.Writer) tw.Writer = writeCloser tw.closers = append(tw.closers, writeCloser) } return nil } // THeaderInfoType is the type id of the info headers. type THeaderInfoType int32 // Supported THeaderInfoType values. const ( _ THeaderInfoType = iota // Skip 0 InfoKeyValue // 1 // Rest of the info types are not supported. ) // THeaderTransport is a Transport mode that implements THeader. // // Note that THeaderTransport handles frame and zlib by itself, // so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), // instead of rich transports like TZlibTransport or TFramedTransport. type THeaderTransport struct { SequenceID int32 Flags uint32 transport TTransport // THeaderMap for read and write readHeaders THeaderMap writeHeaders THeaderMap // Reading related variables. reader *bufio.Reader // When frame is detected, we read the frame fully into frameBuffer. frameBuffer bytes.Buffer // When it's non-nil, Read should read from frameReader instead of // reader, and EOF error indicates end of frame instead of end of all // transport. frameReader io.ReadCloser // Writing related variables writeBuffer bytes.Buffer writeTransforms []THeaderTransformID clientType clientType protocolID THeaderProtocolID cfg *TConfiguration // buffer is used in the following scenarios to avoid repetitive // allocations, while 4 is big enough for all those scenarios: // // * header padding (max size 4) // * write the frame size (size 4) buffer [4]byte } var _ TTransport = (*THeaderTransport)(nil) // Deprecated: Use NewTHeaderTransportConf instead. func NewTHeaderTransport(trans TTransport) *THeaderTransport { return NewTHeaderTransportConf(trans, &TConfiguration{ noPropagation: true, }) } // NewTHeaderTransportConf creates THeaderTransport from the // underlying transport, with given TConfiguration attached. // // If trans is already a *THeaderTransport, it will be returned as is, // but with TConfiguration overridden by the value passed in. // // The protocol ID in TConfiguration is only useful for client transports. // For servers, // the protocol ID will be overridden again to the one set by the client, // to ensure that servers always speak the same dialect as the client. func NewTHeaderTransportConf(trans TTransport, conf *TConfiguration) *THeaderTransport { if ht, ok := trans.(*THeaderTransport); ok { ht.SetTConfiguration(conf) return ht } PropagateTConfiguration(trans, conf) return &THeaderTransport{ transport: trans, reader: bufio.NewReader(trans), writeHeaders: make(THeaderMap), protocolID: conf.GetTHeaderProtocolID(), cfg: conf, } } // Open calls the underlying transport's Open function. func (t *THeaderTransport) Open() error { return t.transport.Open() } // IsOpen calls the underlying transport's IsOpen function. func (t *THeaderTransport) IsOpen() bool { return t.transport.IsOpen() } // ReadFrame tries to read the frame header, guess the client type, and handle // unframed clients. func (t *THeaderTransport) ReadFrame(ctx context.Context) error { if !t.needReadFrame() { // No need to read frame, skipping. return nil } // Peek and handle the first 32 bits. // They could either be the length field of a framed message, // or the first bytes of an unframed message. var buf []byte var err error // This is also usually the first read from a connection, // so handle retries around socket timeouts. _, deadlineSet := ctx.Deadline() for { buf, err = t.reader.Peek(size32) if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { // This is I/O timeout and we still have time, // continue trying continue } // For anything else, do not retry break } if err != nil { return err } frameSize := binary.BigEndian.Uint32(buf) if frameSize&VERSION_MASK == VERSION_1 { t.clientType = clientUnframedBinary return nil } if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { t.clientType = clientUnframedCompact return nil } // At this point it should be a framed message, // sanity check on frameSize then discard the peeked part. if frameSize > THeaderMaxFrameSize || frameSize > uint32(t.cfg.GetMaxFrameSize()) { return NewTProtocolExceptionWithType( SIZE_LIMIT, errors.New("frame too large"), ) } t.reader.Discard(size32) // Read the frame fully into frameBuffer. _, err = io.CopyN(&t.frameBuffer, t.reader, int64(frameSize)) if err != nil { return err } t.frameReader = ioutil.NopCloser(&t.frameBuffer) // Peek and handle the next 32 bits. buf = t.frameBuffer.Bytes()[:size32] version := binary.BigEndian.Uint32(buf) if version&THeaderHeaderMask == THeaderHeaderMagic { t.clientType = clientHeaders return t.parseHeaders(ctx, frameSize) } if version&VERSION_MASK == VERSION_1 { t.clientType = clientFramedBinary return nil } if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { t.clientType = clientFramedCompact return nil } if err := t.endOfFrame(); err != nil { return err } return NewTProtocolExceptionWithType( NOT_IMPLEMENTED, errors.New("unsupported client transport type"), ) } // endOfFrame does end of frame handling. // // It closes frameReader, and also resets frame related states. func (t *THeaderTransport) endOfFrame() error { defer func() { t.frameBuffer.Reset() t.frameReader = nil }() return t.frameReader.Close() } func (t *THeaderTransport) parseHeaders(ctx context.Context, frameSize uint32) error { if t.clientType != clientHeaders { return nil } var err error var meta headerMeta if err = binary.Read(&t.frameBuffer, binary.BigEndian, &meta); err != nil { return err } frameSize -= headerMetaSize t.Flags = meta.MagicFlags & THeaderFlagsMask t.SequenceID = meta.SequenceID headerLength := int64(meta.HeaderLength) * 4 if int64(frameSize) < headerLength { return NewTProtocolExceptionWithType( SIZE_LIMIT, errors.New("header size is larger than the whole frame"), ) } headerBuf := NewTMemoryBuffer() _, err = io.CopyN(headerBuf, &t.frameBuffer, headerLength) if err != nil { return err } hp := NewTCompactProtocol(headerBuf) hp.SetTConfiguration(t.cfg) // At this point the header is already read into headerBuf, // and t.frameBuffer starts from the actual payload. protoID, err := hp.readVarint32() if err != nil { return err } t.protocolID = THeaderProtocolID(protoID) var transformCount int32 transformCount, err = hp.readVarint32() if err != nil { return err } if transformCount > 0 { reader := NewTransformReaderWithCapacity( &t.frameBuffer, int(transformCount), ) t.frameReader = reader transformIDs := make([]THeaderTransformID, transformCount) for i := 0; i < int(transformCount); i++ { id, err := hp.readVarint32() if err != nil { return err } transformIDs[i] = THeaderTransformID(id) } // The transform IDs on the wire was added based on the order of // writing, so on the reading side we need to reverse the order. for i := transformCount - 1; i >= 0; i-- { id := transformIDs[i] if err := reader.AddTransform(id); err != nil { return err } } } // The info part does not use the transforms yet, so it's // important to continue using headerBuf. headers := make(THeaderMap) for { infoType, err := hp.readVarint32() if errors.Is(err, io.EOF) { break } if err != nil { return err } if THeaderInfoType(infoType) == InfoKeyValue { count, err := hp.readVarint32() if err != nil { return err } for i := 0; i < int(count); i++ { key, err := hp.ReadString(ctx) if err != nil { return err } value, err := hp.ReadString(ctx) if err != nil { return err } headers[key] = value } } else { // Skip reading info section on the first // unsupported info type. break } } t.readHeaders = headers return nil } func (t *THeaderTransport) needReadFrame() bool { if t.clientType == clientUnknown { // This is a new connection that's never read before. return true } if t.isFramed() && t.frameReader == nil { // We just finished the last frame. return true } return false } func (t *THeaderTransport) Read(p []byte) (read int, err error) { // Here using context.Background instead of a context passed in is safe. // First is that there's no way to pass context into this function. // Then, 99% of the case when calling this Read frame is already read // into frameReader. ReadFrame here is more of preventing bugs that // didn't call ReadFrame before calling Read. err = t.ReadFrame(context.Background()) if err != nil { return } if t.frameReader != nil { read, err = t.frameReader.Read(p) if err == nil && t.frameBuffer.Len() <= 0 { // the last Read finished the frame, do endOfFrame // handling here. err = t.endOfFrame() } else if err == io.EOF { err = t.endOfFrame() if err != nil { return } if read == 0 { // Try to read the next frame when we hit EOF // (end of frame) immediately. // When we got here, it means the last read // finished the previous frame, but didn't // do endOfFrame handling yet. // We have to read the next frame here, // as otherwise we would return 0 and nil, // which is a case not handled well by most // protocol implementations. return t.Read(p) } } return } return t.reader.Read(p) } // Write writes data to the write buffer. // // You need to call Flush to actually write them to the transport. func (t *THeaderTransport) Write(p []byte) (int, error) { return t.writeBuffer.Write(p) } // Flush writes the appropriate header and the write buffer to the underlying transport. func (t *THeaderTransport) Flush(ctx context.Context) error { if t.writeBuffer.Len() == 0 { return nil } defer t.writeBuffer.Reset() switch t.clientType { default: fallthrough case clientUnknown: t.clientType = clientHeaders fallthrough case clientHeaders: headers := NewTMemoryBuffer() hp := NewTCompactProtocol(headers) hp.SetTConfiguration(t.cfg) if _, err := hp.writeVarint32(int32(t.protocolID)); err != nil { return NewTTransportExceptionFromError(err) } if _, err := hp.writeVarint32(int32(len(t.writeTransforms))); err != nil { return NewTTransportExceptionFromError(err) } for _, transform := range t.writeTransforms { if _, err := hp.writeVarint32(int32(transform)); err != nil { return NewTTransportExceptionFromError(err) } } if len(t.writeHeaders) > 0 { if _, err := hp.writeVarint32(int32(InfoKeyValue)); err != nil { return NewTTransportExceptionFromError(err) } if _, err := hp.writeVarint32(int32(len(t.writeHeaders))); err != nil { return NewTTransportExceptionFromError(err) } for key, value := range t.writeHeaders { if err := hp.WriteString(ctx, key); err != nil { return NewTTransportExceptionFromError(err) } if err := hp.WriteString(ctx, value); err != nil { return NewTTransportExceptionFromError(err) } } } padding := 4 - headers.Len()%4 if padding < 4 { buf := t.buffer[:padding] for i := range buf { buf[i] = 0 } if _, err := headers.Write(buf); err != nil { return NewTTransportExceptionFromError(err) } } var payload bytes.Buffer meta := headerMeta{ MagicFlags: THeaderHeaderMagic + t.Flags&THeaderFlagsMask, SequenceID: t.SequenceID, HeaderLength: uint16(headers.Len() / 4), } if err := binary.Write(&payload, binary.BigEndian, meta); err != nil { return NewTTransportExceptionFromError(err) } if _, err := io.Copy(&payload, headers); err != nil { return NewTTransportExceptionFromError(err) } writer, err := NewTransformWriter(&payload, t.writeTransforms) if err != nil { return NewTTransportExceptionFromError(err) } if _, err := io.Copy(writer, &t.writeBuffer); err != nil { return NewTTransportExceptionFromError(err) } if err := writer.Close(); err != nil { return NewTTransportExceptionFromError(err) } // First write frame length buf := t.buffer[:size32] binary.BigEndian.PutUint32(buf, uint32(payload.Len())) if _, err := t.transport.Write(buf); err != nil { return NewTTransportExceptionFromError(err) } // Then write the payload if _, err := io.Copy(t.transport, &payload); err != nil { return NewTTransportExceptionFromError(err) } case clientFramedBinary, clientFramedCompact: buf := t.buffer[:size32] binary.BigEndian.PutUint32(buf, uint32(t.writeBuffer.Len())) if _, err := t.transport.Write(buf); err != nil { return NewTTransportExceptionFromError(err) } fallthrough case clientUnframedBinary, clientUnframedCompact: if _, err := io.Copy(t.transport, &t.writeBuffer); err != nil { return NewTTransportExceptionFromError(err) } } select { default: case <-ctx.Done(): return NewTTransportExceptionFromError(ctx.Err()) } return t.transport.Flush(ctx) } // Close closes the transport, along with its underlying transport. func (t *THeaderTransport) Close() error { if err := t.Flush(context.Background()); err != nil { return err } return t.transport.Close() } // RemainingBytes calls underlying transport's RemainingBytes. // // Even in framed cases, because of all the possible compression transforms // involved, the remaining frame size is likely to be different from the actual // remaining readable bytes, so we don't bother to keep tracking the remaining // frame size by ourselves and just use the underlying transport's // RemainingBytes directly. func (t *THeaderTransport) RemainingBytes() uint64 { return t.transport.RemainingBytes() } // GetReadHeaders returns the THeaderMap read from transport. func (t *THeaderTransport) GetReadHeaders() THeaderMap { return t.readHeaders } // SetWriteHeader sets a header for write. func (t *THeaderTransport) SetWriteHeader(key, value string) { t.writeHeaders[key] = value } // ClearWriteHeaders clears all write headers previously set. func (t *THeaderTransport) ClearWriteHeaders() { t.writeHeaders = make(THeaderMap) } // AddTransform add a transform for writing. func (t *THeaderTransport) AddTransform(transform THeaderTransformID) error { if !supportedTransformIDs[transform] { return NewTProtocolExceptionWithType( NOT_IMPLEMENTED, fmt.Errorf("THeaderTransformID %d not supported", transform), ) } t.writeTransforms = append(t.writeTransforms, transform) return nil } // Protocol returns the wrapped protocol id used in this THeaderTransport. func (t *THeaderTransport) Protocol() THeaderProtocolID { switch t.clientType { default: return t.protocolID case clientFramedBinary, clientUnframedBinary: return THeaderProtocolBinary case clientFramedCompact, clientUnframedCompact: return THeaderProtocolCompact } } func (t *THeaderTransport) isFramed() bool { switch t.clientType { default: return false case clientHeaders, clientFramedBinary, clientFramedCompact: return true } } // SetTConfiguration implements TConfigurationSetter. func (t *THeaderTransport) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(t.transport, cfg) t.cfg = cfg } // THeaderTransportFactory is a TTransportFactory implementation to create // THeaderTransport. // // It also implements TConfigurationSetter. type THeaderTransportFactory struct { // The underlying factory, could be nil. Factory TTransportFactory cfg *TConfiguration } // Deprecated: Use NewTHeaderTransportFactoryConf instead. func NewTHeaderTransportFactory(factory TTransportFactory) TTransportFactory { return NewTHeaderTransportFactoryConf(factory, &TConfiguration{ noPropagation: true, }) } // NewTHeaderTransportFactoryConf creates a new *THeaderTransportFactory with // the given *TConfiguration. func NewTHeaderTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory { return &THeaderTransportFactory{ Factory: factory, cfg: conf, } } // GetTransport implements TTransportFactory. func (f *THeaderTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if f.Factory != nil { t, err := f.Factory.GetTransport(trans) if err != nil { return nil, err } return NewTHeaderTransportConf(t, f.cfg), nil } return NewTHeaderTransportConf(trans, f.cfg), nil } // SetTConfiguration implements TConfigurationSetter. func (f *THeaderTransportFactory) SetTConfiguration(cfg *TConfiguration) { PropagateTConfiguration(f.Factory, f.cfg) f.cfg = cfg } var ( _ TConfigurationSetter = (*THeaderTransportFactory)(nil) _ TConfigurationSetter = (*THeaderTransport)(nil) ) http_client.go000066400000000000000000000175471414226744000350320ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "context" "errors" "io" "io/ioutil" "net/http" "net/url" "strconv" ) // Default to using the shared http client. Library users are // free to change this global client or specify one through // THttpClientOptions. var DefaultHttpClient *http.Client = http.DefaultClient type THttpClient struct { client *http.Client response *http.Response url *url.URL requestBuffer *bytes.Buffer header http.Header nsecConnectTimeout int64 nsecReadTimeout int64 } type THttpClientTransportFactory struct { options THttpClientOptions url string } func (p *THttpClientTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if trans != nil { t, ok := trans.(*THttpClient) if ok && t.url != nil { return NewTHttpClientWithOptions(t.url.String(), p.options) } } return NewTHttpClientWithOptions(p.url, p.options) } type THttpClientOptions struct { // If nil, DefaultHttpClient is used Client *http.Client } func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory { return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{}) } func NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory { return &THttpClientTransportFactory{url: url, options: options} } func NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) { parsedURL, err := url.Parse(urlstr) if err != nil { return nil, err } buf := make([]byte, 0, 1024) client := options.Client if client == nil { client = DefaultHttpClient } httpHeader := map[string][]string{"Content-Type": {"application/x-thrift"}} return &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil } func NewTHttpClient(urlstr string) (TTransport, error) { return NewTHttpClientWithOptions(urlstr, THttpClientOptions{}) } // Set the HTTP Header for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // httpTrans.SetHeader("User-Agent","Thrift Client 1.0") func (p *THttpClient) SetHeader(key string, value string) { p.header.Add(key, value) } // Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // hdrValue := httpTrans.GetHeader("User-Agent") func (p *THttpClient) GetHeader(key string) string { return p.header.Get(key) } // Deletes the HTTP Header given a Header Key for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // httpTrans.DelHeader("User-Agent") func (p *THttpClient) DelHeader(key string) { p.header.Del(key) } func (p *THttpClient) Open() error { // do nothing return nil } func (p *THttpClient) IsOpen() bool { return p.response != nil || p.requestBuffer != nil } func (p *THttpClient) closeResponse() error { var err error if p.response != nil && p.response.Body != nil { // The docs specify that if keepalive is enabled and the response body is not // read to completion the connection will never be returned to the pool and // reused. Errors are being ignored here because if the connection is invalid // and this fails for some reason, the Close() method will do any remaining // cleanup. io.Copy(ioutil.Discard, p.response.Body) err = p.response.Body.Close() } p.response = nil return err } func (p *THttpClient) Close() error { if p.requestBuffer != nil { p.requestBuffer.Reset() p.requestBuffer = nil } return p.closeResponse() } func (p *THttpClient) Read(buf []byte) (int, error) { if p.response == nil { return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") } n, err := p.response.Body.Read(buf) if n > 0 && (err == nil || errors.Is(err, io.EOF)) { return n, nil } return n, NewTTransportExceptionFromError(err) } func (p *THttpClient) ReadByte() (c byte, err error) { if p.response == nil { return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") } return readByte(p.response.Body) } func (p *THttpClient) Write(buf []byte) (int, error) { if p.requestBuffer == nil { return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") } return p.requestBuffer.Write(buf) } func (p *THttpClient) WriteByte(c byte) error { if p.requestBuffer == nil { return NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") } return p.requestBuffer.WriteByte(c) } func (p *THttpClient) WriteString(s string) (n int, err error) { if p.requestBuffer == nil { return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") } return p.requestBuffer.WriteString(s) } func (p *THttpClient) Flush(ctx context.Context) error { // Close any previous response body to avoid leaking connections. p.closeResponse() // Give up the ownership of the current request buffer to http request, // and create a new buffer for the next request. buf := p.requestBuffer p.requestBuffer = new(bytes.Buffer) req, err := http.NewRequest("POST", p.url.String(), buf) if err != nil { return NewTTransportExceptionFromError(err) } req.Header = p.header if ctx != nil { req = req.WithContext(ctx) } response, err := p.client.Do(req) if err != nil { return NewTTransportExceptionFromError(err) } if response.StatusCode != http.StatusOK { // Close the response to avoid leaking file descriptors. closeResponse does // more than just call Close(), so temporarily assign it and reuse the logic. p.response = response p.closeResponse() // TODO(pomack) log bad response return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+strconv.Itoa(response.StatusCode)) } p.response = response return nil } func (p *THttpClient) RemainingBytes() (num_bytes uint64) { len := p.response.ContentLength if len >= 0 { return uint64(len) } const maxSize = ^uint64(0) return maxSize // the truth is, we just don't know unless framed is used } // Deprecated: Use NewTHttpClientTransportFactory instead. func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory { return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{}) } // Deprecated: Use NewTHttpClientTransportFactoryWithOptions instead. func NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory { return NewTHttpClientTransportFactoryWithOptions(url, options) } // Deprecated: Use NewTHttpClientWithOptions instead. func NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) { return NewTHttpClientWithOptions(urlstr, options) } // Deprecated: Use NewTHttpClient instead. func NewTHttpPostClient(urlstr string) (TTransport, error) { return NewTHttpClientWithOptions(urlstr, THttpClientOptions{}) } http_transport.go000066400000000000000000000041411414226744000355720ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "compress/gzip" "io" "net/http" "strings" "sync" ) // NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function func NewThriftHandlerFunc(processor TProcessor, inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { return gz(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/x-thrift") transport := NewStreamTransport(r.Body, w) processor.Process(r.Context(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) }) } // gz transparently compresses the HTTP response if the client supports it. func gz(handler http.HandlerFunc) http.HandlerFunc { sp := &sync.Pool{ New: func() interface{} { return gzip.NewWriter(nil) }, } return func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { handler(w, r) return } w.Header().Set("Content-Encoding", "gzip") gz := sp.Get().(*gzip.Writer) gz.Reset(w) defer func() { _ = gz.Close() sp.Put(gz) }() gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w} handler(gzw, r) } } type gzipResponseWriter struct { io.Writer http.ResponseWriter } func (w gzipResponseWriter) Write(b []byte) (int, error) { return w.Writer.Write(b) } iostream_transport.go000066400000000000000000000131401414226744000364350ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "context" "io" ) // StreamTransport is a Transport made of an io.Reader and/or an io.Writer type StreamTransport struct { io.Reader io.Writer isReadWriter bool closed bool } type StreamTransportFactory struct { Reader io.Reader Writer io.Writer isReadWriter bool } func (p *StreamTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if trans != nil { t, ok := trans.(*StreamTransport) if ok { if t.isReadWriter { return NewStreamTransportRW(t.Reader.(io.ReadWriter)), nil } if t.Reader != nil && t.Writer != nil { return NewStreamTransport(t.Reader, t.Writer), nil } if t.Reader != nil && t.Writer == nil { return NewStreamTransportR(t.Reader), nil } if t.Reader == nil && t.Writer != nil { return NewStreamTransportW(t.Writer), nil } return &StreamTransport{}, nil } } if p.isReadWriter { return NewStreamTransportRW(p.Reader.(io.ReadWriter)), nil } if p.Reader != nil && p.Writer != nil { return NewStreamTransport(p.Reader, p.Writer), nil } if p.Reader != nil && p.Writer == nil { return NewStreamTransportR(p.Reader), nil } if p.Reader == nil && p.Writer != nil { return NewStreamTransportW(p.Writer), nil } return &StreamTransport{}, nil } func NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory { return &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter} } func NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport { return &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)} } func NewStreamTransportR(r io.Reader) *StreamTransport { return &StreamTransport{Reader: bufio.NewReader(r)} } func NewStreamTransportW(w io.Writer) *StreamTransport { return &StreamTransport{Writer: bufio.NewWriter(w)} } func NewStreamTransportRW(rw io.ReadWriter) *StreamTransport { bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw)) return &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true} } func (p *StreamTransport) IsOpen() bool { return !p.closed } // implicitly opened on creation, can't be reopened once closed func (p *StreamTransport) Open() error { if !p.closed { return NewTTransportException(ALREADY_OPEN, "StreamTransport already open.") } else { return NewTTransportException(NOT_OPEN, "cannot reopen StreamTransport.") } } // Closes both the input and output streams. func (p *StreamTransport) Close() error { if p.closed { return NewTTransportException(NOT_OPEN, "StreamTransport already closed.") } p.closed = true closedReader := false if p.Reader != nil { c, ok := p.Reader.(io.Closer) if ok { e := c.Close() closedReader = true if e != nil { return e } } p.Reader = nil } if p.Writer != nil && (!closedReader || !p.isReadWriter) { c, ok := p.Writer.(io.Closer) if ok { e := c.Close() if e != nil { return e } } p.Writer = nil } return nil } // Flushes the underlying output stream if not null. func (p *StreamTransport) Flush(ctx context.Context) error { if p.Writer == nil { return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream") } f, ok := p.Writer.(Flusher) if ok { err := f.Flush() if err != nil { return NewTTransportExceptionFromError(err) } } return nil } func (p *StreamTransport) Read(c []byte) (n int, err error) { n, err = p.Reader.Read(c) if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) ReadByte() (c byte, err error) { f, ok := p.Reader.(io.ByteReader) if ok { c, err = f.ReadByte() } else { c, err = readByte(p.Reader) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) Write(c []byte) (n int, err error) { n, err = p.Writer.Write(c) if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) WriteByte(c byte) (err error) { f, ok := p.Writer.(io.ByteWriter) if ok { err = f.WriteByte(c) } else { err = writeByte(p.Writer, c) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) WriteString(s string) (n int, err error) { f, ok := p.Writer.(stringWriter) if ok { n, err = f.WriteString(s) } else { n, err = p.Writer.Write([]byte(s)) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the truth is, we just don't know unless framed is used } // SetTConfiguration implements TConfigurationSetter for propagation. func (p *StreamTransport) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(p.Reader, conf) PropagateTConfiguration(p.Writer, conf) } var _ TConfigurationSetter = (*StreamTransport)(nil) json_protocol.go000066400000000000000000000354501414226744000354000ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "encoding/base64" "fmt" ) const ( THRIFT_JSON_PROTOCOL_VERSION = 1 ) // for references to _ParseContext see tsimplejson_protocol.go // JSON protocol implementation for thrift. // Utilizes Simple JSON protocol // type TJSONProtocol struct { *TSimpleJSONProtocol } // Constructor func NewTJSONProtocol(t TTransport) *TJSONProtocol { v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)} v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL) v.dumpContext.push(_CONTEXT_IN_TOPLEVEL) return v } // Factory type TJSONProtocolFactory struct{} func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTJSONProtocol(trans) } func NewTJSONProtocolFactory() *TJSONProtocolFactory { return &TJSONProtocolFactory{} } func (p *TJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteI32(ctx, THRIFT_JSON_PROTOCOL_VERSION); e != nil { return e } if e := p.WriteString(ctx, name); e != nil { return e } if e := p.WriteByte(ctx, int8(typeId)); e != nil { return e } if e := p.WriteI32(ctx, seqId); e != nil { return e } return nil } func (p *TJSONProtocol) WriteMessageEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteStructBegin(ctx context.Context, name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } func (p *TJSONProtocol) WriteStructEnd(ctx context.Context) error { return p.OutputObjectEnd() } func (p *TJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { if e := p.WriteI16(ctx, id); e != nil { return e } if e := p.OutputObjectBegin(); e != nil { return e } s, e1 := p.TypeIdToString(typeId) if e1 != nil { return e1 } if e := p.WriteString(ctx, s); e != nil { return e } return nil } func (p *TJSONProtocol) WriteFieldEnd(ctx context.Context) error { return p.OutputObjectEnd() } func (p *TJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil } func (p *TJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(keyType) if e1 != nil { return e1 } if e := p.WriteString(ctx, s); e != nil { return e } s, e1 = p.TypeIdToString(valueType) if e1 != nil { return e1 } if e := p.WriteString(ctx, s); e != nil { return e } if e := p.WriteI64(ctx, int64(size)); e != nil { return e } return p.OutputObjectBegin() } func (p *TJSONProtocol) WriteMapEnd(ctx context.Context) error { if e := p.OutputObjectEnd(); e != nil { return e } return p.OutputListEnd() } func (p *TJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TJSONProtocol) WriteListEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TJSONProtocol) WriteSetEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteBool(ctx context.Context, b bool) error { if b { return p.WriteI32(ctx, 1) } return p.WriteI32(ctx, 0) } func (p *TJSONProtocol) WriteByte(ctx context.Context, b int8) error { return p.WriteI32(ctx, int32(b)) } func (p *TJSONProtocol) WriteI16(ctx context.Context, v int16) error { return p.WriteI32(ctx, int32(v)) } func (p *TJSONProtocol) WriteI32(ctx context.Context, v int32) error { return p.OutputI64(int64(v)) } func (p *TJSONProtocol) WriteI64(ctx context.Context, v int64) error { return p.OutputI64(int64(v)) } func (p *TJSONProtocol) WriteDouble(ctx context.Context, v float64) error { return p.OutputF64(v) } func (p *TJSONProtocol) WriteString(ctx context.Context, v string) error { return p.OutputString(v) } func (p *TJSONProtocol) WriteBinary(ctx context.Context, v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string // therefore we use base64 encoding to avoid excessive escaping/quoting if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } writer := base64.NewEncoder(base64.StdEncoding, p.writer) if _, e := writer.Write(v); e != nil { p.writer.Reset(p.trans) // THRIFT-3735 return NewTProtocolException(e) } if e := writer.Close(); e != nil { return NewTProtocolException(e) } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } // Reading methods. func (p *TJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } version, err := p.ReadI32(ctx) if err != nil { return name, typeId, seqId, err } if version != THRIFT_JSON_PROTOCOL_VERSION { e := fmt.Errorf("Unknown Protocol version %d, expected version %d", version, THRIFT_JSON_PROTOCOL_VERSION) return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e) } if name, err = p.ReadString(ctx); err != nil { return name, typeId, seqId, err } bTypeId, err := p.ReadByte(ctx) typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } if seqId, err = p.ReadI32(ctx); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } func (p *TJSONProtocol) ReadMessageEnd(ctx context.Context) error { err := p.ParseListEnd() return err } func (p *TJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { _, err = p.ParseObjectStart() return "", err } func (p *TJSONProtocol) ReadStructEnd(ctx context.Context) error { return p.ParseObjectEnd() } func (p *TJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) { b, _ := p.reader.Peek(1) if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] { return "", STOP, -1, nil } fieldId, err := p.ReadI16(ctx) if err != nil { return "", STOP, fieldId, err } if _, err = p.ParseObjectStart(); err != nil { return "", STOP, fieldId, err } sType, err := p.ReadString(ctx) if err != nil { return "", STOP, fieldId, err } fType, err := p.StringToTypeId(sType) return "", fType, fieldId, err } func (p *TJSONProtocol) ReadFieldEnd(ctx context.Context) error { return p.ParseObjectEnd() } func (p *TJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType sKeyType, e := p.ReadString(ctx) if e != nil { return keyType, valueType, size, e } keyType, e = p.StringToTypeId(sKeyType) if e != nil { return keyType, valueType, size, e } // read valueType sValueType, e := p.ReadString(ctx) if e != nil { return keyType, valueType, size, e } valueType, e = p.StringToTypeId(sValueType) if e != nil { return keyType, valueType, size, e } // read size iSize, e := p.ReadI64(ctx) if e != nil { return keyType, valueType, size, e } size = int(iSize) _, e = p.ParseObjectStart() return keyType, valueType, size, e } func (p *TJSONProtocol) ReadMapEnd(ctx context.Context) error { e := p.ParseObjectEnd() if e != nil { return e } return p.ParseListEnd() } func (p *TJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TJSONProtocol) ReadListEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TJSONProtocol) ReadSetEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TJSONProtocol) ReadBool(ctx context.Context) (bool, error) { value, err := p.ReadI32(ctx) return (value != 0), err } func (p *TJSONProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.ReadI64(ctx) return int8(v), err } func (p *TJSONProtocol) ReadI16(ctx context.Context) (int16, error) { v, err := p.ReadI64(ctx) return int16(v), err } func (p *TJSONProtocol) ReadI32(ctx context.Context) (int32, error) { v, err := p.ReadI64(ctx) return int32(v), err } func (p *TJSONProtocol) ReadI64(ctx context.Context) (int64, error) { v, _, err := p.ParseI64() return v, err } func (p *TJSONProtocol) ReadDouble(ctx context.Context) (float64, error) { v, _, err := p.ParseF64() return v, err } func (p *TJSONProtocol) ReadString(ctx context.Context) (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseStringBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseBase64EncodedBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TJSONProtocol) Flush(ctx context.Context) (err error) { err = p.writer.Flush() if err == nil { err = p.trans.Flush(ctx) } return NewTProtocolException(err) } func (p *TJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) { return SkipDefaultDepth(ctx, p, fieldType) } func (p *TJSONProtocol) Transport() TTransport { return p.trans } func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(elemType) if e1 != nil { return e1 } if e := p.OutputString(s); e != nil { return e } if e := p.OutputI64(int64(size)); e != nil { return e } return nil } func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } // We don't really use the ctx in ReadString implementation, // so this is safe for now. // We might want to add context to ParseElemListBegin if we start to use // ctx in ReadString implementation in the future. sElemType, err := p.ReadString(context.Background()) if err != nil { return VOID, size, err } elemType, err = p.StringToTypeId(sElemType) if err != nil { return elemType, size, err } nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } // We don't really use the ctx in ReadString implementation, // so this is safe for now. // We might want to add context to ParseElemListBegin if we start to use // ctx in ReadString implementation in the future. sElemType, err := p.ReadString(context.Background()) if err != nil { return VOID, size, err } elemType, err = p.StringToTypeId(sElemType) if err != nil { return elemType, size, err } nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(elemType) if e1 != nil { return e1 } if e := p.OutputString(s); e != nil { return e } if e := p.OutputI64(int64(size)); e != nil { return e } return nil } func (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) { switch byte(fieldType) { case BOOL: return "tf", nil case BYTE: return "i8", nil case I16: return "i16", nil case I32: return "i32", nil case I64: return "i64", nil case DOUBLE: return "dbl", nil case STRING: return "str", nil case STRUCT: return "rec", nil case MAP: return "map", nil case SET: return "set", nil case LIST: return "lst", nil } e := fmt.Errorf("Unknown fieldType: %d", int(fieldType)) return "", NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) { switch fieldType { case "tf": return TType(BOOL), nil case "i8": return TType(BYTE), nil case "i16": return TType(I16), nil case "i32": return TType(I32), nil case "i64": return TType(I64), nil case "dbl": return TType(DOUBLE), nil case "str": return TType(STRING), nil case "rec": return TType(STRUCT), nil case "map": return TType(MAP), nil case "set": return TType(SET), nil case "lst": return TType(LIST), nil } e := fmt.Errorf("Unknown type identifier: %s", fieldType) return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e) } var _ TConfigurationSetter = (*TJSONProtocol)(nil) golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/logger.go000066400000000000000000000037461414226744000340470ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "log" "os" "testing" ) // Logger is a simple wrapper of a logging function. // // In reality the users might actually use different logging libraries, and they // are not always compatible with each other. // // Logger is meant to be a simple common ground that it's easy to wrap whatever // logging library they use into. // // See https://issues.apache.org/jira/browse/THRIFT-4985 for the design // discussion behind it. type Logger func(msg string) // NopLogger is a Logger implementation that does nothing. func NopLogger(msg string) {} // StdLogger wraps stdlib log package into a Logger. // // If logger passed in is nil, it will fallback to use stderr and default flags. func StdLogger(logger *log.Logger) Logger { if logger == nil { logger = log.New(os.Stderr, "", log.LstdFlags) } return func(msg string) { logger.Print(msg) } } // TestLogger is a Logger implementation can be used in test codes. // // It fails the test when being called. func TestLogger(tb testing.TB) Logger { return func(msg string) { tb.Errorf("logger called with msg: %q", msg) } } func fallbackLogger(logger Logger) Logger { if logger == nil { return StdLogger(nil) } return logger } memory_buffer.go000066400000000000000000000040321414226744000353370ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "context" ) // Memory buffer-based implementation of the TTransport interface. type TMemoryBuffer struct { *bytes.Buffer size int } type TMemoryBufferTransportFactory struct { size int } func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if trans != nil { t, ok := trans.(*TMemoryBuffer) if ok && t.size > 0 { return NewTMemoryBufferLen(t.size), nil } } return NewTMemoryBufferLen(p.size), nil } func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { return &TMemoryBufferTransportFactory{size: size} } func NewTMemoryBuffer() *TMemoryBuffer { return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0} } func NewTMemoryBufferLen(size int) *TMemoryBuffer { buf := make([]byte, 0, size) return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size} } func (p *TMemoryBuffer) IsOpen() bool { return true } func (p *TMemoryBuffer) Open() error { return nil } func (p *TMemoryBuffer) Close() error { p.Buffer.Reset() return nil } // Flushing a memory buffer is a no-op func (p *TMemoryBuffer) Flush(ctx context.Context) error { return nil } func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) { return uint64(p.Buffer.Len()) } messagetype.go000066400000000000000000000021221414226744000350220ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Message type constants in the Thrift protocol. type TMessageType int32 const ( INVALID_TMESSAGE_TYPE TMessageType = 0 CALL TMessageType = 1 REPLY TMessageType = 2 EXCEPTION TMessageType = 3 ONEWAY TMessageType = 4 ) middleware.go000066400000000000000000000076211414226744000346220ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "context" // ProcessorMiddleware is a function that can be passed to WrapProcessor to wrap the // TProcessorFunctions for that TProcessor. // // Middlewares are passed in the name of the function as set in the processor // map of the TProcessor. type ProcessorMiddleware func(name string, next TProcessorFunction) TProcessorFunction // WrapProcessor takes an existing TProcessor and wraps each of its inner // TProcessorFunctions with the middlewares passed in and returns it. // // Middlewares will be called in the order that they are defined: // // 1. Middlewares[0] // 2. Middlewares[1] // ... // N. Middlewares[n] func WrapProcessor(processor TProcessor, middlewares ...ProcessorMiddleware) TProcessor { for name, processorFunc := range processor.ProcessorMap() { wrapped := processorFunc // Add middlewares in reverse so the first in the list is the outermost. for i := len(middlewares) - 1; i >= 0; i-- { wrapped = middlewares[i](name, wrapped) } processor.AddToProcessorMap(name, wrapped) } return processor } // WrappedTProcessorFunction is a convenience struct that implements the // TProcessorFunction interface that can be used when implementing custom // Middleware. type WrappedTProcessorFunction struct { // Wrapped is called by WrappedTProcessorFunction.Process and should be a // "wrapped" call to a base TProcessorFunc.Process call. Wrapped func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) } // Process implements the TProcessorFunction interface using p.Wrapped. func (p WrappedTProcessorFunction) Process(ctx context.Context, seqID int32, in, out TProtocol) (bool, TException) { return p.Wrapped(ctx, seqID, in, out) } // verify that WrappedTProcessorFunction implements TProcessorFunction var ( _ TProcessorFunction = WrappedTProcessorFunction{} _ TProcessorFunction = (*WrappedTProcessorFunction)(nil) ) // ClientMiddleware can be passed to WrapClient in order to wrap TClient calls // with custom middleware. type ClientMiddleware func(TClient) TClient // WrappedTClient is a convenience struct that implements the TClient interface // using inner Wrapped function. // // This is provided to aid in developing ClientMiddleware. type WrappedTClient struct { Wrapped func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) } // Call implements the TClient interface by calling and returning c.Wrapped. func (c WrappedTClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { return c.Wrapped(ctx, method, args, result) } // verify that WrappedTClient implements TClient var ( _ TClient = WrappedTClient{} _ TClient = (*WrappedTClient)(nil) ) // WrapClient wraps the given TClient in the given middlewares. // // Middlewares will be called in the order that they are defined: // // 1. Middlewares[0] // 2. Middlewares[1] // ... // N. Middlewares[n] func WrapClient(client TClient, middlewares ...ClientMiddleware) TClient { // Add middlewares in reverse so the first in the list is the outermost. for i := len(middlewares) - 1; i >= 0; i-- { client = middlewares[i](client) } return client } multiplexed_protocol.go000066400000000000000000000201111414226744000367470ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "fmt" "strings" ) /* TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift client to communicate with a multiplexing Thrift server, by prepending the service name to the function name during function calls. NOTE: THIS IS NOT USED BY SERVERS. On the server, use TMultiplexedProcessor to handle request from a multiplexing client. This example uses a single socket transport to invoke two services: socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT) transport := thrift.NewTFramedTransport(socket) protocol := thrift.NewTBinaryProtocolTransport(transport) mp := thrift.NewTMultiplexedProtocol(protocol, "Calculator") service := Calculator.NewCalculatorClient(mp) mp2 := thrift.NewTMultiplexedProtocol(protocol, "WeatherReport") service2 := WeatherReport.NewWeatherReportClient(mp2) err := transport.Open() if err != nil { t.Fatal("Unable to open client socket", err) } fmt.Println(service.Add(2,2)) fmt.Println(service2.GetTemperature()) */ type TMultiplexedProtocol struct { TProtocol serviceName string } const MULTIPLEXED_SEPARATOR = ":" func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol { return &TMultiplexedProtocol{ TProtocol: protocol, serviceName: serviceName, } } func (t *TMultiplexedProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { if typeId == CALL || typeId == ONEWAY { return t.TProtocol.WriteMessageBegin(ctx, t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid) } else { return t.TProtocol.WriteMessageBegin(ctx, name, typeId, seqid) } } /* TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services. To do so, you instantiate the processor and then register additional processors with it, as shown in the following example: var processor = thrift.NewTMultiplexedProcessor() firstProcessor := processor.RegisterProcessor("FirstService", firstProcessor) processor.registerProcessor( "Calculator", Calculator.NewCalculatorProcessor(&CalculatorHandler{}), ) processor.registerProcessor( "WeatherReport", WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}), ) serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT) if err != nil { t.Fatal("Unable to create server socket", err) } server := thrift.NewTSimpleServer2(processor, serverTransport) server.Serve(); */ type TMultiplexedProcessor struct { serviceProcessorMap map[string]TProcessor DefaultProcessor TProcessor } func NewTMultiplexedProcessor() *TMultiplexedProcessor { return &TMultiplexedProcessor{ serviceProcessorMap: make(map[string]TProcessor), } } // ProcessorMap returns a mapping of "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}" // to TProcessorFunction for any registered processors. If there is also a // DefaultProcessor, the keys for the methods on that processor will simply be // "{FunctionName}". If the TMultiplexedProcessor has both a DefaultProcessor and // other registered processors, then the keys will be a mix of both formats. // // The implementation differs with other TProcessors in that the map returned is // a new map, while most TProcessors just return their internal mapping directly. // This means that edits to the map returned by this implementation of ProcessorMap // will not affect the underlying mapping within the TMultiplexedProcessor. func (t *TMultiplexedProcessor) ProcessorMap() map[string]TProcessorFunction { processorFuncMap := make(map[string]TProcessorFunction) for name, processor := range t.serviceProcessorMap { for method, processorFunc := range processor.ProcessorMap() { processorFuncName := name + MULTIPLEXED_SEPARATOR + method processorFuncMap[processorFuncName] = processorFunc } } if t.DefaultProcessor != nil { for method, processorFunc := range t.DefaultProcessor.ProcessorMap() { processorFuncMap[method] = processorFunc } } return processorFuncMap } // AddToProcessorMap updates the underlying TProcessor ProccessorMaps depending on // the format of "name". // // If "name" is in the format "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}", // then it sets the given TProcessorFunction on the inner TProcessor with the // ProcessorName component using the FunctionName component. // // If "name" is just in the format "{FunctionName}", that is to say there is no // MULTIPLEXED_SEPARATOR, and the TMultiplexedProcessor has a DefaultProcessor // configured, then it will set the given TProcessorFunction on the DefaultProcessor // using the given name. // // If there is not a TProcessor available for the given name, then this function // does nothing. This can happen when there is no TProcessor registered for // the given ProcessorName or if all that is given is the FunctionName and there // is no DefaultProcessor set. func (t *TMultiplexedProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) { components := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) if len(components) != 2 { if t.DefaultProcessor != nil && len(components) == 1 { t.DefaultProcessor.AddToProcessorMap(components[0], processorFunc) } return } processorName := components[0] funcName := components[1] if processor, ok := t.serviceProcessorMap[processorName]; ok { processor.AddToProcessorMap(funcName, processorFunc) } } // verify that TMultiplexedProcessor implements TProcessor var _ TProcessor = (*TMultiplexedProcessor)(nil) func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) { t.DefaultProcessor = processor } func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) { if t.serviceProcessorMap == nil { t.serviceProcessorMap = make(map[string]TProcessor) } t.serviceProcessorMap[name] = processor } func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { name, typeId, seqid, err := in.ReadMessageBegin(ctx) if err != nil { return false, NewTProtocolException(err) } if typeId != CALL && typeId != ONEWAY { return false, NewTProtocolException(fmt.Errorf("Unexpected message type %v", typeId)) } //extract the service name v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) if len(v) != 2 { if t.DefaultProcessor != nil { smb := NewStoredMessageProtocol(in, name, typeId, seqid) return t.DefaultProcessor.Process(ctx, smb, out) } return false, NewTProtocolException(fmt.Errorf( "Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name, )) } actualProcessor, ok := t.serviceProcessorMap[v[0]] if !ok { return false, NewTProtocolException(fmt.Errorf( "Service name not found: %s. Did you forget to call registerProcessor()?", v[0], )) } smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) return actualProcessor.Process(ctx, smb, out) } //Protocol that use stored message for ReadMessageBegin type storedMessageProtocol struct { TProtocol name string typeId TMessageType seqid int32 } func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol { return &storedMessageProtocol{protocol, name, typeId, seqid} } func (s *storedMessageProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) { return s.name, s.typeId, s.seqid, nil } numeric.go000066400000000000000000000076051414226744000341510ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "math" "strconv" ) type Numeric interface { Int64() int64 Int32() int32 Int16() int16 Byte() byte Int() int Float64() float64 Float32() float32 String() string isNull() bool } type numeric struct { iValue int64 dValue float64 sValue string isNil bool } var ( INFINITY Numeric NEGATIVE_INFINITY Numeric NAN Numeric ZERO Numeric NUMERIC_NULL Numeric ) func NewNumericFromDouble(dValue float64) Numeric { if math.IsInf(dValue, 1) { return INFINITY } if math.IsInf(dValue, -1) { return NEGATIVE_INFINITY } if math.IsNaN(dValue) { return NAN } iValue := int64(dValue) sValue := strconv.FormatFloat(dValue, 'g', 10, 64) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI64(iValue int64) Numeric { dValue := float64(iValue) sValue := strconv.FormatInt(iValue, 10) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI32(iValue int32) Numeric { dValue := float64(iValue) sValue := strconv.FormatInt(int64(iValue), 10) isNil := false return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromString(sValue string) Numeric { if sValue == INFINITY.String() { return INFINITY } if sValue == NEGATIVE_INFINITY.String() { return NEGATIVE_INFINITY } if sValue == NAN.String() { return NAN } iValue, _ := strconv.ParseInt(sValue, 10, 64) dValue, _ := strconv.ParseFloat(sValue, 64) isNil := len(sValue) == 0 return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromJSONString(sValue string, isNull bool) Numeric { if isNull { return NewNullNumeric() } if sValue == JSON_INFINITY { return INFINITY } if sValue == JSON_NEGATIVE_INFINITY { return NEGATIVE_INFINITY } if sValue == JSON_NAN { return NAN } iValue, _ := strconv.ParseInt(sValue, 10, 64) dValue, _ := strconv.ParseFloat(sValue, 64) return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull} } func NewNullNumeric() Numeric { return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true} } func (p *numeric) Int64() int64 { return p.iValue } func (p *numeric) Int32() int32 { return int32(p.iValue) } func (p *numeric) Int16() int16 { return int16(p.iValue) } func (p *numeric) Byte() byte { return byte(p.iValue) } func (p *numeric) Int() int { return int(p.iValue) } func (p *numeric) Float64() float64 { return p.dValue } func (p *numeric) Float32() float32 { return float32(p.dValue) } func (p *numeric) String() string { return p.sValue } func (p *numeric) isNull() bool { return p.isNil } func init() { INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false} NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false} NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false} ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false} NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true} } pointerize.go000066400000000000000000000041411414226744000346670ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift /////////////////////////////////////////////////////////////////////////////// // This file is home to helpers that convert from various base types to // respective pointer types. This is necessary because Go does not permit // references to constants, nor can a pointer type to base type be allocated // and initialized in a single expression. // // E.g., this is not allowed: // // var ip *int = &5 // // But this *is* allowed: // // func IntPtr(i int) *int { return &i } // var ip *int = IntPtr(5) // // Since pointers to base types are commonplace as [optional] fields in // exported thrift structs, we factor such helpers here. /////////////////////////////////////////////////////////////////////////////// func Float32Ptr(v float32) *float32 { return &v } func Float64Ptr(v float64) *float64 { return &v } func IntPtr(v int) *int { return &v } func Int8Ptr(v int8) *int8 { return &v } func Int16Ptr(v int16) *int16 { return &v } func Int32Ptr(v int32) *int32 { return &v } func Int64Ptr(v int64) *int64 { return &v } func StringPtr(v string) *string { return &v } func Uint32Ptr(v uint32) *uint32 { return &v } func Uint64Ptr(v uint64) *uint64 { return &v } func BoolPtr(v bool) *bool { return &v } func ByteSlicePtr(v []byte) *[]byte { return &v } processor_factory.go000066400000000000000000000046471414226744000362600ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "context" // A processor is a generic object which operates upon an input stream and // writes to some output stream. type TProcessor interface { Process(ctx context.Context, in, out TProtocol) (bool, TException) // ProcessorMap returns a map of thrift method names to TProcessorFunctions. ProcessorMap() map[string]TProcessorFunction // AddToProcessorMap adds the given TProcessorFunction to the internal // processor map at the given key. // // If one is already set at the given key, it will be replaced with the new // TProcessorFunction. AddToProcessorMap(string, TProcessorFunction) } type TProcessorFunction interface { Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) } // The default processor factory just returns a singleton // instance. type TProcessorFactory interface { GetProcessor(trans TTransport) TProcessor } type tProcessorFactory struct { processor TProcessor } func NewTProcessorFactory(p TProcessor) TProcessorFactory { return &tProcessorFactory{processor: p} } func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor { return p.processor } /** * The default processor factory just returns a singleton * instance. */ type TProcessorFunctionFactory interface { GetProcessorFunction(trans TTransport) TProcessorFunction } type tProcessorFunctionFactory struct { processor TProcessorFunction } func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory { return &tProcessorFunctionFactory{processor: p} } func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction { return p.processor } protocol.go000066400000000000000000000130241414226744000343400ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "errors" "fmt" ) const ( VERSION_MASK = 0xffff0000 VERSION_1 = 0x80010000 ) type TProtocol interface { WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error WriteMessageEnd(ctx context.Context) error WriteStructBegin(ctx context.Context, name string) error WriteStructEnd(ctx context.Context) error WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error WriteFieldEnd(ctx context.Context) error WriteFieldStop(ctx context.Context) error WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error WriteMapEnd(ctx context.Context) error WriteListBegin(ctx context.Context, elemType TType, size int) error WriteListEnd(ctx context.Context) error WriteSetBegin(ctx context.Context, elemType TType, size int) error WriteSetEnd(ctx context.Context) error WriteBool(ctx context.Context, value bool) error WriteByte(ctx context.Context, value int8) error WriteI16(ctx context.Context, value int16) error WriteI32(ctx context.Context, value int32) error WriteI64(ctx context.Context, value int64) error WriteDouble(ctx context.Context, value float64) error WriteString(ctx context.Context, value string) error WriteBinary(ctx context.Context, value []byte) error ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) ReadMessageEnd(ctx context.Context) error ReadStructBegin(ctx context.Context) (name string, err error) ReadStructEnd(ctx context.Context) error ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) ReadFieldEnd(ctx context.Context) error ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) ReadMapEnd(ctx context.Context) error ReadListBegin(ctx context.Context) (elemType TType, size int, err error) ReadListEnd(ctx context.Context) error ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) ReadSetEnd(ctx context.Context) error ReadBool(ctx context.Context) (value bool, err error) ReadByte(ctx context.Context) (value int8, err error) ReadI16(ctx context.Context) (value int16, err error) ReadI32(ctx context.Context) (value int32, err error) ReadI64(ctx context.Context) (value int64, err error) ReadDouble(ctx context.Context) (value float64, err error) ReadString(ctx context.Context) (value string, err error) ReadBinary(ctx context.Context) (value []byte, err error) Skip(ctx context.Context, fieldType TType) (err error) Flush(ctx context.Context) (err error) Transport() TTransport } // The maximum recursive depth the skip() function will traverse const DEFAULT_RECURSION_DEPTH = 64 // Skips over the next data element from the provided input TProtocol object. func SkipDefaultDepth(ctx context.Context, prot TProtocol, typeId TType) (err error) { return Skip(ctx, prot, typeId, DEFAULT_RECURSION_DEPTH) } // Skips over the next data element from the provided input TProtocol object. func Skip(ctx context.Context, self TProtocol, fieldType TType, maxDepth int) (err error) { if maxDepth <= 0 { return NewTProtocolExceptionWithType(DEPTH_LIMIT, errors.New("Depth limit exceeded")) } switch fieldType { case BOOL: _, err = self.ReadBool(ctx) return case BYTE: _, err = self.ReadByte(ctx) return case I16: _, err = self.ReadI16(ctx) return case I32: _, err = self.ReadI32(ctx) return case I64: _, err = self.ReadI64(ctx) return case DOUBLE: _, err = self.ReadDouble(ctx) return case STRING: _, err = self.ReadString(ctx) return case STRUCT: if _, err = self.ReadStructBegin(ctx); err != nil { return err } for { _, typeId, _, _ := self.ReadFieldBegin(ctx) if typeId == STOP { break } err := Skip(ctx, self, typeId, maxDepth-1) if err != nil { return err } self.ReadFieldEnd(ctx) } return self.ReadStructEnd(ctx) case MAP: keyType, valueType, size, err := self.ReadMapBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { err := Skip(ctx, self, keyType, maxDepth-1) if err != nil { return err } self.Skip(ctx, valueType) } return self.ReadMapEnd(ctx) case SET: elemType, size, err := self.ReadSetBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } return self.ReadSetEnd(ctx) case LIST: elemType, size, err := self.ReadListBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } return self.ReadListEnd(ctx) default: return NewTProtocolExceptionWithType(INVALID_DATA, errors.New(fmt.Sprintf("Unknown data type %d", fieldType))) } return nil } protocol_exception.go000066400000000000000000000045561414226744000364300ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/base64" "errors" ) // Thrift Protocol exception type TProtocolException interface { TException TypeId() int } const ( UNKNOWN_PROTOCOL_EXCEPTION = 0 INVALID_DATA = 1 NEGATIVE_SIZE = 2 SIZE_LIMIT = 3 BAD_VERSION = 4 NOT_IMPLEMENTED = 5 DEPTH_LIMIT = 6 ) type tProtocolException struct { typeId int err error msg string } var _ TProtocolException = (*tProtocolException)(nil) func (tProtocolException) TExceptionType() TExceptionType { return TExceptionTypeProtocol } func (p *tProtocolException) TypeId() int { return p.typeId } func (p *tProtocolException) String() string { return p.msg } func (p *tProtocolException) Error() string { return p.msg } func (p *tProtocolException) Unwrap() error { return p.err } func NewTProtocolException(err error) TProtocolException { if err == nil { return nil } if e, ok := err.(TProtocolException); ok { return e } if errors.As(err, new(base64.CorruptInputError)) { return NewTProtocolExceptionWithType(INVALID_DATA, err) } return NewTProtocolExceptionWithType(UNKNOWN_PROTOCOL_EXCEPTION, err) } func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { if err == nil { return nil } return &tProtocolException{ typeId: errType, err: err, msg: err.Error(), } } func prependTProtocolException(prepend string, err TProtocolException) TProtocolException { return &tProtocolException{ typeId: err.TypeId(), err: err, msg: prepend + err.Error(), } } protocol_factory.go000066400000000000000000000016741414226744000360770ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Factory interface for constructing protocol instances. type TProtocolFactory interface { GetProtocol(trans TTransport) TProtocol } response_helper.go000066400000000000000000000057531414226744000357060ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" ) // See https://godoc.org/context#WithValue on why do we need the unexported typedefs. type responseHelperKey struct{} // TResponseHelper defines a object with a set of helper functions that can be // retrieved from the context object passed into server handler functions. // // Use GetResponseHelper to retrieve the injected TResponseHelper implementation // from the context object. // // The zero value of TResponseHelper is valid with all helper functions being // no-op. type TResponseHelper struct { // THeader related functions *THeaderResponseHelper } // THeaderResponseHelper defines THeader related TResponseHelper functions. // // The zero value of *THeaderResponseHelper is valid with all helper functions // being no-op. type THeaderResponseHelper struct { proto *THeaderProtocol } // NewTHeaderResponseHelper creates a new THeaderResponseHelper from the // underlying TProtocol. func NewTHeaderResponseHelper(proto TProtocol) *THeaderResponseHelper { if hp, ok := proto.(*THeaderProtocol); ok { return &THeaderResponseHelper{ proto: hp, } } return nil } // SetHeader sets a response header. // // It's no-op if the underlying protocol/transport does not support THeader. func (h *THeaderResponseHelper) SetHeader(key, value string) { if h != nil && h.proto != nil { h.proto.SetWriteHeader(key, value) } } // ClearHeaders clears all the response headers previously set. // // It's no-op if the underlying protocol/transport does not support THeader. func (h *THeaderResponseHelper) ClearHeaders() { if h != nil && h.proto != nil { h.proto.ClearWriteHeaders() } } // GetResponseHelper retrieves the TResponseHelper implementation injected into // the context object. // // If no helper was found in the context object, a nop helper with ok == false // will be returned. func GetResponseHelper(ctx context.Context) (helper TResponseHelper, ok bool) { if v := ctx.Value(responseHelperKey{}); v != nil { helper, ok = v.(TResponseHelper) } return } // SetResponseHelper injects TResponseHelper into the context object. func SetResponseHelper(ctx context.Context, helper TResponseHelper) context.Context { return context.WithValue(ctx, responseHelperKey{}, helper) } rich_transport.go000066400000000000000000000034001414226744000355350ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "io" ) type RichTransport struct { TTransport } // Wraps Transport to provide TRichTransport interface func NewTRichTransport(trans TTransport) *RichTransport { return &RichTransport{trans} } func (r *RichTransport) ReadByte() (c byte, err error) { return readByte(r.TTransport) } func (r *RichTransport) WriteByte(c byte) error { return writeByte(r.TTransport, c) } func (r *RichTransport) WriteString(s string) (n int, err error) { return r.Write([]byte(s)) } func (r *RichTransport) RemainingBytes() (num_bytes uint64) { return r.TTransport.RemainingBytes() } func readByte(r io.Reader) (c byte, err error) { v := [1]byte{0} n, err := r.Read(v[0:1]) if n > 0 && (err == nil || errors.Is(err, io.EOF)) { return v[0], nil } if n > 0 && err != nil { return v[0], err } if err != nil { return 0, err } return v[0], nil } func writeByte(w io.Writer, c byte) error { v := [1]byte{c} _, err := w.Write(v[0:1]) return err } serializer.go000066400000000000000000000065271414226744000346620ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "sync" ) type TSerializer struct { Transport *TMemoryBuffer Protocol TProtocol } type TStruct interface { Write(ctx context.Context, p TProtocol) error Read(ctx context.Context, p TProtocol) error } func NewTSerializer() *TSerializer { transport := NewTMemoryBufferLen(1024) protocol := NewTBinaryProtocolTransport(transport) return &TSerializer{ Transport: transport, Protocol: protocol, } } func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) { t.Transport.Reset() if err = msg.Write(ctx, t.Protocol); err != nil { return } if err = t.Protocol.Flush(ctx); err != nil { return } if err = t.Transport.Flush(ctx); err != nil { return } return t.Transport.String(), nil } func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) { t.Transport.Reset() if err = msg.Write(ctx, t.Protocol); err != nil { return } if err = t.Protocol.Flush(ctx); err != nil { return } if err = t.Transport.Flush(ctx); err != nil { return } b = append(b, t.Transport.Bytes()...) return } // TSerializerPool is the thread-safe version of TSerializer, it uses resource // pool of TSerializer under the hood. // // It must be initialized with either NewTSerializerPool or // NewTSerializerPoolSizeFactory. type TSerializerPool struct { pool sync.Pool } // NewTSerializerPool creates a new TSerializerPool. // // NewTSerializer can be used as the arg here. func NewTSerializerPool(f func() *TSerializer) *TSerializerPool { return &TSerializerPool{ pool: sync.Pool{ New: func() interface{} { return f() }, }, } } // NewTSerializerPoolSizeFactory creates a new TSerializerPool with the given // size and protocol factory. // // Note that the size is not the limit. The TMemoryBuffer underneath can grow // larger than that. It just dictates the initial size. func NewTSerializerPoolSizeFactory(size int, factory TProtocolFactory) *TSerializerPool { return &TSerializerPool{ pool: sync.Pool{ New: func() interface{} { transport := NewTMemoryBufferLen(size) protocol := factory.GetProtocol(transport) return &TSerializer{ Transport: transport, Protocol: protocol, } }, }, } } func (t *TSerializerPool) WriteString(ctx context.Context, msg TStruct) (string, error) { s := t.pool.Get().(*TSerializer) defer t.pool.Put(s) return s.WriteString(ctx, msg) } func (t *TSerializerPool) Write(ctx context.Context, msg TStruct) ([]byte, error) { s := t.pool.Get().(*TSerializer) defer t.pool.Put(s) return s.Write(ctx, msg) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/server.go000066400000000000000000000023701414226744000340660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift type TServer interface { ProcessorFactory() TProcessorFactory ServerTransport() TServerTransport InputTransportFactory() TTransportFactory OutputTransportFactory() TTransportFactory InputProtocolFactory() TProtocolFactory OutputProtocolFactory() TProtocolFactory // Starts the server Serve() error // Stops the server. This is optional on a per-implementation basis. Not // all servers are required to be cleanly stoppable. Stop() error } server_socket.go000066400000000000000000000063331414226744000353620ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net" "sync" "time" ) type TServerSocket struct { listener net.Listener addr net.Addr clientTimeout time.Duration // Protects the interrupted value to make it thread safe. mu sync.RWMutex interrupted bool } func NewTServerSocket(listenAddr string) (*TServerSocket, error) { return NewTServerSocketTimeout(listenAddr, 0) } func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) { addr, err := net.ResolveTCPAddr("tcp", listenAddr) if err != nil { return nil, err } return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil } // Creates a TServerSocket from a net.Addr func NewTServerSocketFromAddrTimeout(addr net.Addr, clientTimeout time.Duration) *TServerSocket { return &TServerSocket{addr: addr, clientTimeout: clientTimeout} } func (p *TServerSocket) Listen() error { p.mu.Lock() defer p.mu.Unlock() if p.IsListening() { return nil } l, err := net.Listen(p.addr.Network(), p.addr.String()) if err != nil { return err } p.listener = l return nil } func (p *TServerSocket) Accept() (TTransport, error) { p.mu.RLock() interrupted := p.interrupted p.mu.RUnlock() if interrupted { return nil, errTransportInterrupted } p.mu.Lock() listener := p.listener p.mu.Unlock() if listener == nil { return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") } conn, err := listener.Accept() if err != nil { return nil, NewTTransportExceptionFromError(err) } return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil } // Checks whether the socket is listening. func (p *TServerSocket) IsListening() bool { return p.listener != nil } // Connects the socket, creating a new socket object if necessary. func (p *TServerSocket) Open() error { p.mu.Lock() defer p.mu.Unlock() if p.IsListening() { return NewTTransportException(ALREADY_OPEN, "Server socket already open") } if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil { return err } else { p.listener = l } return nil } func (p *TServerSocket) Addr() net.Addr { if p.listener != nil { return p.listener.Addr() } return p.addr } func (p *TServerSocket) Close() error { var err error p.mu.Lock() if p.IsListening() { err = p.listener.Close() p.listener = nil } p.mu.Unlock() return err } func (p *TServerSocket) Interrupt() error { p.mu.Lock() p.interrupted = true p.mu.Unlock() p.Close() return nil } server_transport.go000066400000000000000000000024461414226744000361270ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Server transport. Object which provides client transports. type TServerTransport interface { Listen() error Accept() (TTransport, error) Close() error // Optional method implementation. This signals to the server transport // that it should break out of any accept() or listen() that it is currently // blocked on. This method, if implemented, MUST be thread safe, as it may // be called from a different thread context than the other TServerTransport // methods. Interrupt() error } simple_json_protocol.go000066400000000000000000001100301414226744000367350ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "bytes" "context" "encoding/base64" "encoding/json" "errors" "fmt" "io" "math" "strconv" ) type _ParseContext int const ( _CONTEXT_INVALID _ParseContext = iota _CONTEXT_IN_TOPLEVEL // 1 _CONTEXT_IN_LIST_FIRST // 2 _CONTEXT_IN_LIST // 3 _CONTEXT_IN_OBJECT_FIRST // 4 _CONTEXT_IN_OBJECT_NEXT_KEY // 5 _CONTEXT_IN_OBJECT_NEXT_VALUE // 6 ) func (p _ParseContext) String() string { switch p { case _CONTEXT_IN_TOPLEVEL: return "TOPLEVEL" case _CONTEXT_IN_LIST_FIRST: return "LIST-FIRST" case _CONTEXT_IN_LIST: return "LIST" case _CONTEXT_IN_OBJECT_FIRST: return "OBJECT-FIRST" case _CONTEXT_IN_OBJECT_NEXT_KEY: return "OBJECT-NEXT-KEY" case _CONTEXT_IN_OBJECT_NEXT_VALUE: return "OBJECT-NEXT-VALUE" } return "UNKNOWN-PARSE-CONTEXT" } type jsonContextStack []_ParseContext func (s *jsonContextStack) push(v _ParseContext) { *s = append(*s, v) } func (s jsonContextStack) peek() (v _ParseContext, ok bool) { l := len(s) if l <= 0 { return } return s[l-1], true } func (s *jsonContextStack) pop() (v _ParseContext, ok bool) { l := len(*s) if l <= 0 { return } v = (*s)[l-1] *s = (*s)[0 : l-1] return v, true } var errEmptyJSONContextStack = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Unexpected empty json protocol context stack")) // Simple JSON protocol implementation for thrift. // // This protocol produces/consumes a simple output format // suitable for parsing by scripting languages. It should not be // confused with the full-featured TJSONProtocol. // type TSimpleJSONProtocol struct { trans TTransport parseContextStack jsonContextStack dumpContext jsonContextStack writer *bufio.Writer reader *bufio.Reader } // Constructor func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol { v := &TSimpleJSONProtocol{trans: t, writer: bufio.NewWriter(t), reader: bufio.NewReader(t), } v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL) v.dumpContext.push(_CONTEXT_IN_TOPLEVEL) return v } // Factory type TSimpleJSONProtocolFactory struct{} func (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTSimpleJSONProtocol(trans) } func NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory { return &TSimpleJSONProtocolFactory{} } var ( JSON_COMMA []byte JSON_COLON []byte JSON_LBRACE []byte JSON_RBRACE []byte JSON_LBRACKET []byte JSON_RBRACKET []byte JSON_QUOTE byte JSON_QUOTE_BYTES []byte JSON_NULL []byte JSON_TRUE []byte JSON_FALSE []byte JSON_INFINITY string JSON_NEGATIVE_INFINITY string JSON_NAN string JSON_INFINITY_BYTES []byte JSON_NEGATIVE_INFINITY_BYTES []byte JSON_NAN_BYTES []byte json_nonbase_map_elem_bytes []byte ) func init() { JSON_COMMA = []byte{','} JSON_COLON = []byte{':'} JSON_LBRACE = []byte{'{'} JSON_RBRACE = []byte{'}'} JSON_LBRACKET = []byte{'['} JSON_RBRACKET = []byte{']'} JSON_QUOTE = '"' JSON_QUOTE_BYTES = []byte{'"'} JSON_NULL = []byte{'n', 'u', 'l', 'l'} JSON_TRUE = []byte{'t', 'r', 'u', 'e'} JSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'} JSON_INFINITY = "Infinity" JSON_NEGATIVE_INFINITY = "-Infinity" JSON_NAN = "NaN" JSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} JSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} JSON_NAN_BYTES = []byte{'N', 'a', 'N'} json_nonbase_map_elem_bytes = []byte{']', ',', '['} } func jsonQuote(s string) string { b, _ := json.Marshal(s) s1 := string(b) return s1 } func jsonUnquote(s string) (string, bool) { s1 := new(string) err := json.Unmarshal([]byte(s), s1) return *s1, err == nil } func mismatch(expected, actual string) error { return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual) } func (p *TSimpleJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteString(ctx, name); e != nil { return e } if e := p.WriteByte(ctx, int8(typeId)); e != nil { return e } if e := p.WriteI32(ctx, seqId); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteMessageEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteStructBegin(ctx context.Context, name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteStructEnd(ctx context.Context) error { return p.OutputObjectEnd() } func (p *TSimpleJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { if e := p.WriteString(ctx, name); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteFieldEnd(ctx context.Context) error { return nil } func (p *TSimpleJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil } func (p *TSimpleJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteByte(ctx, int8(keyType)); e != nil { return e } if e := p.WriteByte(ctx, int8(valueType)); e != nil { return e } return p.WriteI32(ctx, int32(size)) } func (p *TSimpleJSONProtocol) WriteMapEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TSimpleJSONProtocol) WriteListEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TSimpleJSONProtocol) WriteSetEnd(ctx context.Context) error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteBool(ctx context.Context, b bool) error { return p.OutputBool(b) } func (p *TSimpleJSONProtocol) WriteByte(ctx context.Context, b int8) error { return p.WriteI32(ctx, int32(b)) } func (p *TSimpleJSONProtocol) WriteI16(ctx context.Context, v int16) error { return p.WriteI32(ctx, int32(v)) } func (p *TSimpleJSONProtocol) WriteI32(ctx context.Context, v int32) error { return p.OutputI64(int64(v)) } func (p *TSimpleJSONProtocol) WriteI64(ctx context.Context, v int64) error { return p.OutputI64(int64(v)) } func (p *TSimpleJSONProtocol) WriteDouble(ctx context.Context, v float64) error { return p.OutputF64(v) } func (p *TSimpleJSONProtocol) WriteString(ctx context.Context, v string) error { return p.OutputString(v) } func (p *TSimpleJSONProtocol) WriteBinary(ctx context.Context, v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string // therefore we use base64 encoding to avoid excessive escaping/quoting if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } writer := base64.NewEncoder(base64.StdEncoding, p.writer) if _, e := writer.Write(v); e != nil { p.writer.Reset(p.trans) // THRIFT-3735 return NewTProtocolException(e) } if e := writer.Close(); e != nil { return NewTProtocolException(e) } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } // Reading methods. func (p *TSimpleJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } if name, err = p.ReadString(ctx); err != nil { return name, typeId, seqId, err } bTypeId, err := p.ReadByte(ctx) typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } if seqId, err = p.ReadI32(ctx); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } func (p *TSimpleJSONProtocol) ReadMessageEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { _, err = p.ParseObjectStart() return "", err } func (p *TSimpleJSONProtocol) ReadStructEnd(ctx context.Context) error { return p.ParseObjectEnd() } func (p *TSimpleJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) { if err := p.ParsePreValue(); err != nil { return "", STOP, 0, err } b, _ := p.reader.Peek(1) if len(b) > 0 { switch b[0] { case JSON_RBRACE[0]: return "", STOP, 0, nil case JSON_QUOTE: p.reader.ReadByte() name, err := p.ParseStringBody() // simplejson is not meant to be read back into thrift // - see http://wiki.apache.org/thrift/ThriftUsageJava // - use JSON instead if err != nil { return name, STOP, 0, err } return name, STOP, -1, p.ParsePostValue() } e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b)) return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e) } return "", STOP, 0, NewTProtocolException(io.EOF) } func (p *TSimpleJSONProtocol) ReadFieldEnd(ctx context.Context) error { return nil } func (p *TSimpleJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType bKeyType, e := p.ReadByte(ctx) keyType = TType(bKeyType) if e != nil { return keyType, valueType, size, e } // read valueType bValueType, e := p.ReadByte(ctx) valueType = TType(bValueType) if e != nil { return keyType, valueType, size, e } // read size iSize, err := p.ReadI64(ctx) size = int(iSize) return keyType, valueType, size, err } func (p *TSimpleJSONProtocol) ReadMapEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TSimpleJSONProtocol) ReadListEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TSimpleJSONProtocol) ReadSetEnd(ctx context.Context) error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadBool(ctx context.Context) (bool, error) { var value bool if err := p.ParsePreValue(); err != nil { return value, err } f, _ := p.reader.Peek(1) if len(f) > 0 { switch f[0] { case JSON_TRUE[0]: b := make([]byte, len(JSON_TRUE)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_TRUE) { value = true } else { e := fmt.Errorf("Expected \"true\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } break case JSON_FALSE[0]: b := make([]byte, len(JSON_FALSE)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_FALSE) { value = false } else { e := fmt.Errorf("Expected \"false\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } break case JSON_NULL[0]: b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_NULL) { value = false } else { e := fmt.Errorf("Expected \"null\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } default: e := fmt.Errorf("Expected \"true\", \"false\", or \"null\" but found: %s", string(f)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } } return value, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.ReadI64(ctx) return int8(v), err } func (p *TSimpleJSONProtocol) ReadI16(ctx context.Context) (int16, error) { v, err := p.ReadI64(ctx) return int16(v), err } func (p *TSimpleJSONProtocol) ReadI32(ctx context.Context) (int32, error) { v, err := p.ReadI64(ctx) return int32(v), err } func (p *TSimpleJSONProtocol) ReadI64(ctx context.Context) (int64, error) { v, _, err := p.ParseI64() return v, err } func (p *TSimpleJSONProtocol) ReadDouble(ctx context.Context) (float64, error) { v, _, err := p.ParseF64() return v, err } func (p *TSimpleJSONProtocol) ReadString(ctx context.Context) (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseStringBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseBase64EncodedBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TSimpleJSONProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.writer.Flush()) } func (p *TSimpleJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) { return SkipDefaultDepth(ctx, p, fieldType) } func (p *TSimpleJSONProtocol) Transport() TTransport { return p.trans } func (p *TSimpleJSONProtocol) OutputPreValue() error { cxt, ok := p.dumpContext.peek() if !ok { return errEmptyJSONContextStack } switch cxt { case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: if _, e := p.write(JSON_COMMA); e != nil { return NewTProtocolException(e) } case _CONTEXT_IN_OBJECT_NEXT_VALUE: if _, e := p.write(JSON_COLON); e != nil { return NewTProtocolException(e) } } return nil } func (p *TSimpleJSONProtocol) OutputPostValue() error { cxt, ok := p.dumpContext.peek() if !ok { return errEmptyJSONContextStack } switch cxt { case _CONTEXT_IN_LIST_FIRST: p.dumpContext.pop() p.dumpContext.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST: p.dumpContext.pop() p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_KEY: p.dumpContext.pop() p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: p.dumpContext.pop() p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } func (p *TSimpleJSONProtocol) OutputBool(value bool) error { if e := p.OutputPreValue(); e != nil { return e } var v string if value { v = string(JSON_TRUE) } else { v = string(JSON_FALSE) } cxt, ok := p.dumpContext.peek() if !ok { return errEmptyJSONContextStack } switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputNull() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_NULL); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputF64(value float64) error { if e := p.OutputPreValue(); e != nil { return e } var v string if math.IsNaN(value) { v = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE) } else if math.IsInf(value, 1) { v = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE) } else if math.IsInf(value, -1) { v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) } else { cxt, ok := p.dumpContext.peek() if !ok { return errEmptyJSONContextStack } v = strconv.FormatFloat(value, 'g', -1, 64) switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = string(JSON_QUOTE) + v + string(JSON_QUOTE) } } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputI64(value int64) error { if e := p.OutputPreValue(); e != nil { return e } cxt, ok := p.dumpContext.peek() if !ok { return errEmptyJSONContextStack } v := strconv.FormatInt(value, 10) switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputString(s string) error { if e := p.OutputPreValue(); e != nil { return e } if e := p.OutputStringData(jsonQuote(s)); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputStringData(s string) error { _, e := p.write([]byte(s)) return NewTProtocolException(e) } func (p *TSimpleJSONProtocol) OutputObjectBegin() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_LBRACE); e != nil { return NewTProtocolException(e) } p.dumpContext.push(_CONTEXT_IN_OBJECT_FIRST) return nil } func (p *TSimpleJSONProtocol) OutputObjectEnd() error { if _, e := p.write(JSON_RBRACE); e != nil { return NewTProtocolException(e) } _, ok := p.dumpContext.pop() if !ok { return errEmptyJSONContextStack } if e := p.OutputPostValue(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) OutputListBegin() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_LBRACKET); e != nil { return NewTProtocolException(e) } p.dumpContext.push(_CONTEXT_IN_LIST_FIRST) return nil } func (p *TSimpleJSONProtocol) OutputListEnd() error { if _, e := p.write(JSON_RBRACKET); e != nil { return NewTProtocolException(e) } _, ok := p.dumpContext.pop() if !ok { return errEmptyJSONContextStack } if e := p.OutputPostValue(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } if e := p.OutputI64(int64(elemType)); e != nil { return e } if e := p.OutputI64(int64(size)); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) ParsePreValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } cxt, ok := p.parseContextStack.peek() if !ok { return errEmptyJSONContextStack } b, _ := p.reader.Peek(1) switch cxt { case _CONTEXT_IN_LIST: if len(b) > 0 { switch b[0] { case JSON_RBRACKET[0]: return nil case JSON_COMMA[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \"]\" or \",\" in list context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } case _CONTEXT_IN_OBJECT_NEXT_KEY: if len(b) > 0 { switch b[0] { case JSON_RBRACE[0]: return nil case JSON_COMMA[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \"}\" or \",\" in object context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } case _CONTEXT_IN_OBJECT_NEXT_VALUE: if len(b) > 0 { switch b[0] { case JSON_COLON[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \":\" in object context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } } return nil } func (p *TSimpleJSONProtocol) ParsePostValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } cxt, ok := p.parseContextStack.peek() if !ok { return errEmptyJSONContextStack } switch cxt { case _CONTEXT_IN_LIST_FIRST: p.parseContextStack.pop() p.parseContextStack.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: p.parseContextStack.pop() p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: p.parseContextStack.pop() p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } func (p *TSimpleJSONProtocol) readNonSignificantWhitespace() error { for { b, _ := p.reader.Peek(1) if len(b) < 1 { return nil } switch b[0] { case ' ', '\r', '\n', '\t': p.reader.ReadByte() continue default: break } break } return nil } func (p *TSimpleJSONProtocol) ParseStringBody() (string, error) { line, err := p.reader.ReadString(JSON_QUOTE) if err != nil { return "", NewTProtocolException(err) } l := len(line) // count number of escapes to see if we need to keep going i := 1 for ; i < l; i++ { if line[l-i-1] != '\\' { break } } if i&0x01 == 1 { v, ok := jsonUnquote(string(JSON_QUOTE) + line) if !ok { return "", NewTProtocolException(err) } return v, nil } s, err := p.ParseQuotedStringBody() if err != nil { return "", NewTProtocolException(err) } str := string(JSON_QUOTE) + line + s v, ok := jsonUnquote(str) if !ok { e := fmt.Errorf("Unable to parse as JSON string %s", str) return "", NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, nil } func (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, error) { line, err := p.reader.ReadString(JSON_QUOTE) if err != nil { return "", NewTProtocolException(err) } l := len(line) // count number of escapes to see if we need to keep going i := 1 for ; i < l; i++ { if line[l-i-1] != '\\' { break } } if i&0x01 == 1 { return line, nil } s, err := p.ParseQuotedStringBody() if err != nil { return "", NewTProtocolException(err) } v := line + s return v, nil } func (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, error) { line, err := p.reader.ReadBytes(JSON_QUOTE) if err != nil { return line, NewTProtocolException(err) } line2 := line[0 : len(line)-1] l := len(line2) if (l % 4) != 0 { pad := 4 - (l % 4) fill := [...]byte{'=', '=', '='} line2 = append(line2, fill[:pad]...) l = len(line2) } output := make([]byte, base64.StdEncoding.DecodedLen(l)) n, err := base64.StdEncoding.Decode(output, line2) return output[0:n], NewTProtocolException(err) } func (p *TSimpleJSONProtocol) ParseI64() (int64, bool, error) { if err := p.ParsePreValue(); err != nil { return 0, false, err } var value int64 var isnull bool if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) isnull = true } else { num, err := p.readNumeric() isnull = (num == nil) if !isnull { value = num.Int64() } if err != nil { return value, isnull, err } } return value, isnull, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseF64() (float64, bool, error) { if err := p.ParsePreValue(); err != nil { return 0, false, err } var value float64 var isnull bool if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) isnull = true } else { num, err := p.readNumeric() isnull = (num == nil) if !isnull { value = num.Float64() } if err != nil { return value, isnull, err } } return value, isnull, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) { if err := p.ParsePreValue(); err != nil { return false, err } var b []byte b, err := p.reader.Peek(1) if err != nil { return false, err } if len(b) > 0 && b[0] == JSON_LBRACE[0] { p.reader.ReadByte() p.parseContextStack.push(_CONTEXT_IN_OBJECT_FIRST) return false, nil } else if p.safePeekContains(JSON_NULL) { return true, nil } e := fmt.Errorf("Expected '{' or null, but found '%s'", string(b)) return false, NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TSimpleJSONProtocol) ParseObjectEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } cxt, _ := p.parseContextStack.peek() if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) { e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) } line, err := p.reader.ReadString(JSON_RBRACE[0]) if err != nil { return NewTProtocolException(err) } for _, char := range line { switch char { default: e := fmt.Errorf("Expecting end of object \"}\", but found: \"%s\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', '}': break } } p.parseContextStack.pop() return p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) { if e := p.ParsePreValue(); e != nil { return false, e } var b []byte b, err = p.reader.Peek(1) if err != nil { return false, err } if len(b) >= 1 && b[0] == JSON_LBRACKET[0] { p.parseContextStack.push(_CONTEXT_IN_LIST_FIRST) p.reader.ReadByte() isNull = false } else if p.safePeekContains(JSON_NULL) { isNull = true } else { err = fmt.Errorf("Expected \"null\" or \"[\", received %q", b) } return isNull, NewTProtocolExceptionWithType(INVALID_DATA, err) } func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } bElemType, _, err := p.ParseI64() elemType = TType(bElemType) if err != nil { return elemType, size, err } nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } func (p *TSimpleJSONProtocol) ParseListEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } cxt, _ := p.parseContextStack.peek() if cxt != _CONTEXT_IN_LIST { e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) } line, err := p.reader.ReadString(JSON_RBRACKET[0]) if err != nil { return NewTProtocolException(err) } for _, char := range line { switch char { default: e := fmt.Errorf("Expecting end of list \"]\", but found: \"%v\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]): break } } p.parseContextStack.pop() if cxt, ok := p.parseContextStack.peek(); !ok { return errEmptyJSONContextStack } else if cxt == _CONTEXT_IN_TOPLEVEL { return nil } return p.ParsePostValue() } func (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, error) { e := p.readNonSignificantWhitespace() if e != nil { return nil, VOID, NewTProtocolException(e) } b, e := p.reader.Peek(1) if len(b) > 0 { c := b[0] switch c { case JSON_NULL[0]: buf := make([]byte, len(JSON_NULL)) _, e := p.reader.Read(buf) if e != nil { return nil, VOID, NewTProtocolException(e) } if string(JSON_NULL) != string(buf) { e = mismatch(string(JSON_NULL), string(buf)) return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } return nil, VOID, nil case JSON_QUOTE: p.reader.ReadByte() v, e := p.ParseStringBody() if e != nil { return v, UTF8, NewTProtocolException(e) } if v == JSON_INFINITY { return INFINITY, DOUBLE, nil } else if v == JSON_NEGATIVE_INFINITY { return NEGATIVE_INFINITY, DOUBLE, nil } else if v == JSON_NAN { return NAN, DOUBLE, nil } return v, UTF8, nil case JSON_TRUE[0]: buf := make([]byte, len(JSON_TRUE)) _, e := p.reader.Read(buf) if e != nil { return true, BOOL, NewTProtocolException(e) } if string(JSON_TRUE) != string(buf) { e := mismatch(string(JSON_TRUE), string(buf)) return true, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return true, BOOL, nil case JSON_FALSE[0]: buf := make([]byte, len(JSON_FALSE)) _, e := p.reader.Read(buf) if e != nil { return false, BOOL, NewTProtocolException(e) } if string(JSON_FALSE) != string(buf) { e := mismatch(string(JSON_FALSE), string(buf)) return false, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return false, BOOL, nil case JSON_LBRACKET[0]: _, e := p.reader.ReadByte() return make([]interface{}, 0), LIST, NewTProtocolException(e) case JSON_LBRACE[0]: _, e := p.reader.ReadByte() return make(map[string]interface{}), STRUCT, NewTProtocolException(e) case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]: // assume numeric v, e := p.readNumeric() return v, DOUBLE, e default: e := fmt.Errorf("Expected element in list but found '%s' while parsing JSON.", string(c)) return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } } e = fmt.Errorf("Cannot read a single element while parsing JSON.") return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TSimpleJSONProtocol) readIfNull() (bool, error) { cont := true for cont { b, _ := p.reader.Peek(1) if len(b) < 1 { return false, nil } switch b[0] { default: return false, nil case JSON_NULL[0]: cont = false break case ' ', '\n', '\r', '\t': p.reader.ReadByte() break } } if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) return true, nil } return false, nil } func (p *TSimpleJSONProtocol) readQuoteIfNext() { b, _ := p.reader.Peek(1) if len(b) > 0 && b[0] == JSON_QUOTE { p.reader.ReadByte() } } func (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) { isNull, err := p.readIfNull() if isNull || err != nil { return NUMERIC_NULL, err } hasDecimalPoint := false nextCanBeSign := true hasE := false MAX_LEN := 40 buf := bytes.NewBuffer(make([]byte, 0, MAX_LEN)) continueFor := true inQuotes := false for continueFor { c, err := p.reader.ReadByte() if err != nil { if err == io.EOF { break } return NUMERIC_NULL, NewTProtocolException(err) } switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': buf.WriteByte(c) nextCanBeSign = false case '.': if hasDecimalPoint { e := fmt.Errorf("Unable to parse number with multiple decimal points '%s.'", buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if hasE { e := fmt.Errorf("Unable to parse number with decimal points in the exponent '%s.'", buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) hasDecimalPoint, nextCanBeSign = true, false case 'e', 'E': if hasE { e := fmt.Errorf("Unable to parse number with multiple exponents '%s%c'", buf.String(), c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) hasE, nextCanBeSign = true, true case '-', '+': if !nextCanBeSign { e := fmt.Errorf("Negative sign within number") return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) nextCanBeSign = false case ' ', 0, '\t', '\n', '\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]: p.reader.UnreadByte() continueFor = false case JSON_NAN[0]: if buf.Len() == 0 { buffer := make([]byte, len(JSON_NAN)) buffer[0] = c _, e := p.reader.Read(buffer[1:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_NAN != string(buffer) { e := mismatch(JSON_NAN, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return NAN, nil } else { e := fmt.Errorf("Unable to parse number starting with character '%c'", c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } case JSON_INFINITY[0]: if buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') { buffer := make([]byte, len(JSON_INFINITY)) buffer[0] = c _, e := p.reader.Read(buffer[1:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_INFINITY != string(buffer) { e := mismatch(JSON_INFINITY, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return INFINITY, nil } else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] { buffer := make([]byte, len(JSON_NEGATIVE_INFINITY)) buffer[0] = JSON_NEGATIVE_INFINITY[0] buffer[1] = c _, e := p.reader.Read(buffer[2:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_NEGATIVE_INFINITY != string(buffer) { e := mismatch(JSON_NEGATIVE_INFINITY, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return NEGATIVE_INFINITY, nil } else { e := fmt.Errorf("Unable to parse number starting with character '%c' due to existing buffer %s", c, buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } case JSON_QUOTE: if !inQuotes { inQuotes = true } else { break } default: e := fmt.Errorf("Unable to parse number starting with character '%c'", c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } } if buf.Len() == 0 { e := fmt.Errorf("Unable to parse number from empty string ''") return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return NewNumericFromJSONString(buf.String(), false), nil } // Safely peeks into the buffer, reading only what is necessary func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { for i := 0; i < len(b); i++ { a, _ := p.reader.Peek(i + 1) if len(a) < (i+1) || a[i] != b[i] { return false } } return true } // Reset the context stack to its initial state. func (p *TSimpleJSONProtocol) resetContextStack() { p.parseContextStack = jsonContextStack{_CONTEXT_IN_TOPLEVEL} p.dumpContext = jsonContextStack{_CONTEXT_IN_TOPLEVEL} } func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { n, err := p.writer.Write(b) if err != nil { p.writer.Reset(p.trans) // THRIFT-3735 } return n, err } // SetTConfiguration implements TConfigurationSetter for propagation. func (p *TSimpleJSONProtocol) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(p.trans, conf) } var _ TConfigurationSetter = (*TSimpleJSONProtocol)(nil) simple_server.go000066400000000000000000000230051414226744000353560ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "fmt" "io" "sync" "sync/atomic" "time" ) // ErrAbandonRequest is a special error server handler implementations can // return to indicate that the request has been abandoned. // // TSimpleServer will check for this error, and close the client connection // instead of writing the response/error back to the client. // // It shall only be used when the server handler implementation know that the // client already abandoned the request (by checking that the passed in context // is already canceled, for example). var ErrAbandonRequest = errors.New("request abandoned") // ServerConnectivityCheckInterval defines the ticker interval used by // connectivity check in thrift compiled TProcessorFunc implementations. // // It's defined as a variable instead of constant, so that thrift server // implementations can change its value to control the behavior. // // If it's changed to <=0, the feature will be disabled. var ServerConnectivityCheckInterval = time.Millisecond * 5 /* * This is not a typical TSimpleServer as it is not blocked after accept a socket. * It is more like a TThreadedServer that can handle different connections in different goroutines. * This will work if golang user implements a conn-pool like thing in client side. */ type TSimpleServer struct { closed int32 wg sync.WaitGroup mu sync.Mutex processorFactory TProcessorFactory serverTransport TServerTransport inputTransportFactory TTransportFactory outputTransportFactory TTransportFactory inputProtocolFactory TProtocolFactory outputProtocolFactory TProtocolFactory // Headers to auto forward in THeaderProtocol forwardHeaders []string logger Logger } func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer { return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport) } func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory4(NewTProcessorFactory(processor), serverTransport, transportFactory, protocolFactory, ) } func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory6(NewTProcessorFactory(processor), serverTransport, inputTransportFactory, outputTransportFactory, inputProtocolFactory, outputProtocolFactory, ) } func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer { return NewTSimpleServerFactory6(processorFactory, serverTransport, NewTTransportFactory(), NewTTransportFactory(), NewTBinaryProtocolFactoryDefault(), NewTBinaryProtocolFactoryDefault(), ) } func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory6(processorFactory, serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, ) } func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { return &TSimpleServer{ processorFactory: processorFactory, serverTransport: serverTransport, inputTransportFactory: inputTransportFactory, outputTransportFactory: outputTransportFactory, inputProtocolFactory: inputProtocolFactory, outputProtocolFactory: outputProtocolFactory, } } func (p *TSimpleServer) ProcessorFactory() TProcessorFactory { return p.processorFactory } func (p *TSimpleServer) ServerTransport() TServerTransport { return p.serverTransport } func (p *TSimpleServer) InputTransportFactory() TTransportFactory { return p.inputTransportFactory } func (p *TSimpleServer) OutputTransportFactory() TTransportFactory { return p.outputTransportFactory } func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory { return p.inputProtocolFactory } func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory { return p.outputProtocolFactory } func (p *TSimpleServer) Listen() error { return p.serverTransport.Listen() } // SetForwardHeaders sets the list of header keys that will be auto forwarded // while using THeaderProtocol. // // "forward" means that when the server is also a client to other upstream // thrift servers, the context object user gets in the processor functions will // have both read and write headers set, with write headers being forwarded. // Users can always override the write headers by calling SetWriteHeaderList // before calling thrift client functions. func (p *TSimpleServer) SetForwardHeaders(headers []string) { size := len(headers) if size == 0 { p.forwardHeaders = nil return } keys := make([]string, size) copy(keys, headers) p.forwardHeaders = keys } // SetLogger sets the logger used by this TSimpleServer. // // If no logger was set before Serve is called, a default logger using standard // log library will be used. func (p *TSimpleServer) SetLogger(logger Logger) { p.logger = logger } func (p *TSimpleServer) innerAccept() (int32, error) { client, err := p.serverTransport.Accept() p.mu.Lock() defer p.mu.Unlock() closed := atomic.LoadInt32(&p.closed) if closed != 0 { return closed, nil } if err != nil { return 0, err } if client != nil { p.wg.Add(1) go func() { defer p.wg.Done() if err := p.processRequests(client); err != nil { p.logger(fmt.Sprintf("error processing request: %v", err)) } }() } return 0, nil } func (p *TSimpleServer) AcceptLoop() error { for { closed, err := p.innerAccept() if err != nil { return err } if closed != 0 { return nil } } } func (p *TSimpleServer) Serve() error { p.logger = fallbackLogger(p.logger) err := p.Listen() if err != nil { return err } p.AcceptLoop() return nil } func (p *TSimpleServer) Stop() error { p.mu.Lock() defer p.mu.Unlock() if atomic.LoadInt32(&p.closed) != 0 { return nil } atomic.StoreInt32(&p.closed, 1) p.serverTransport.Interrupt() p.wg.Wait() return nil } // If err is actually EOF, return nil, otherwise return err as-is. func treatEOFErrorsAsNil(err error) error { if err == nil { return nil } if errors.Is(err, io.EOF) { return nil } var te TTransportException if errors.As(err, &te) && te.TypeId() == END_OF_FILE { return nil } return err } func (p *TSimpleServer) processRequests(client TTransport) (err error) { defer func() { err = treatEOFErrorsAsNil(err) }() processor := p.processorFactory.GetProcessor(client) inputTransport, err := p.inputTransportFactory.GetTransport(client) if err != nil { return err } inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) var outputTransport TTransport var outputProtocol TProtocol // for THeaderProtocol, we must use the same protocol instance for // input and output so that the response is in the same dialect that // the server detected the request was in. headerProtocol, ok := inputProtocol.(*THeaderProtocol) if ok { outputProtocol = inputProtocol } else { oTrans, err := p.outputTransportFactory.GetTransport(client) if err != nil { return err } outputTransport = oTrans outputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport) } if inputTransport != nil { defer inputTransport.Close() } if outputTransport != nil { defer outputTransport.Close() } for { if atomic.LoadInt32(&p.closed) != 0 { return nil } ctx := SetResponseHelper( defaultCtx, TResponseHelper{ THeaderResponseHelper: NewTHeaderResponseHelper(outputProtocol), }, ) if headerProtocol != nil { // We need to call ReadFrame here, otherwise we won't // get any headers on the AddReadTHeaderToContext call. // // ReadFrame is safe to be called multiple times so it // won't break when it's called again later when we // actually start to read the message. if err := headerProtocol.ReadFrame(ctx); err != nil { return err } ctx = AddReadTHeaderToContext(ctx, headerProtocol.GetReadHeaders()) ctx = SetWriteHeaderList(ctx, p.forwardHeaders) } ok, err := processor.Process(ctx, inputProtocol, outputProtocol) if errors.Is(err, ErrAbandonRequest) { return client.Close() } if errors.As(err, new(TTransportException)) && err != nil { return err } var tae TApplicationException if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD { continue } if !ok { break } } return nil } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/socket.go000066400000000000000000000140571414226744000340550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "net" "time" ) type TSocket struct { conn *socketConn addr net.Addr cfg *TConfiguration connectTimeout time.Duration socketTimeout time.Duration } // Deprecated: Use NewTSocketConf instead. func NewTSocket(hostPort string) (*TSocket, error) { return NewTSocketConf(hostPort, &TConfiguration{ noPropagation: true, }) } // NewTSocketConf creates a net.Conn-backed TTransport, given a host and port. // // Example: // // trans, err := thrift.NewTSocketConf("localhost:9090", &TConfiguration{ // ConnectTimeout: time.Second, // Use 0 for no timeout // SocketTimeout: time.Second, // Use 0 for no timeout // }) func NewTSocketConf(hostPort string, conf *TConfiguration) (*TSocket, error) { addr, err := net.ResolveTCPAddr("tcp", hostPort) if err != nil { return nil, err } return NewTSocketFromAddrConf(addr, conf), nil } // Deprecated: Use NewTSocketConf instead. func NewTSocketTimeout(hostPort string, connTimeout time.Duration, soTimeout time.Duration) (*TSocket, error) { return NewTSocketConf(hostPort, &TConfiguration{ ConnectTimeout: connTimeout, SocketTimeout: soTimeout, noPropagation: true, }) } // NewTSocketFromAddrConf creates a TSocket from a net.Addr func NewTSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSocket { return &TSocket{ addr: addr, cfg: conf, } } // Deprecated: Use NewTSocketFromAddrConf instead. func NewTSocketFromAddrTimeout(addr net.Addr, connTimeout time.Duration, soTimeout time.Duration) *TSocket { return NewTSocketFromAddrConf(addr, &TConfiguration{ ConnectTimeout: connTimeout, SocketTimeout: soTimeout, noPropagation: true, }) } // NewTSocketFromConnConf creates a TSocket from an existing net.Conn. func NewTSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSocket { return &TSocket{ conn: wrapSocketConn(conn), addr: conn.RemoteAddr(), cfg: conf, } } // Deprecated: Use NewTSocketFromConnConf instead. func NewTSocketFromConnTimeout(conn net.Conn, socketTimeout time.Duration) *TSocket { return NewTSocketFromConnConf(conn, &TConfiguration{ SocketTimeout: socketTimeout, noPropagation: true, }) } // SetTConfiguration implements TConfigurationSetter. // // It can be used to set connect and socket timeouts. func (p *TSocket) SetTConfiguration(conf *TConfiguration) { p.cfg = conf } // Sets the connect timeout func (p *TSocket) SetConnTimeout(timeout time.Duration) error { if p.cfg == nil { p.cfg = &TConfiguration{ noPropagation: true, } } p.cfg.ConnectTimeout = timeout return nil } // Sets the socket timeout func (p *TSocket) SetSocketTimeout(timeout time.Duration) error { if p.cfg == nil { p.cfg = &TConfiguration{ noPropagation: true, } } p.cfg.SocketTimeout = timeout return nil } func (p *TSocket) pushDeadline(read, write bool) { var t time.Time if timeout := p.cfg.GetSocketTimeout(); timeout > 0 { t = time.Now().Add(time.Duration(timeout)) } if read && write { p.conn.SetDeadline(t) } else if read { p.conn.SetReadDeadline(t) } else if write { p.conn.SetWriteDeadline(t) } } // Connects the socket, creating a new socket object if necessary. func (p *TSocket) Open() error { if p.conn.isValid() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { return NewTTransportException(NOT_OPEN, "Cannot open nil address.") } if len(p.addr.Network()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") } if len(p.addr.String()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } var err error if p.conn, err = createSocketConnFromReturn(net.DialTimeout( p.addr.Network(), p.addr.String(), p.cfg.GetConnectTimeout(), )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } return nil } // Retrieve the underlying net.Conn func (p *TSocket) Conn() net.Conn { return p.conn } // Returns true if the connection is open func (p *TSocket) IsOpen() bool { return p.conn.IsOpen() } // Closes the socket. func (p *TSocket) Close() error { // Close the socket if p.conn != nil { err := p.conn.Close() if err != nil { return err } p.conn = nil } return nil } //Returns the remote address of the socket. func (p *TSocket) Addr() net.Addr { return p.addr } func (p *TSocket) Read(buf []byte) (int, error) { if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between // p.pushDeadline and p.conn.Read could cause the deadline set inside // p.pushDeadline being reset, thus need to be avoided. n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSocket) Write(buf []byte) (int, error) { if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) return p.conn.Write(buf) } func (p *TSocket) Flush(ctx context.Context) error { return nil } func (p *TSocket) Interrupt() error { if !p.conn.isValid() { return nil } return p.conn.Close() } func (p *TSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the truth is, we just don't know unless framed is used } var _ TConfigurationSetter = (*TSocket)(nil) socket_conn.go000066400000000000000000000060421414226744000350060ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net" ) // socketConn is a wrapped net.Conn that tries to do connectivity check. type socketConn struct { net.Conn buffer [1]byte } var _ net.Conn = (*socketConn)(nil) // createSocketConnFromReturn is a language sugar to help create socketConn from // return values of functions like net.Dial, tls.Dial, net.Listener.Accept, etc. func createSocketConnFromReturn(conn net.Conn, err error) (*socketConn, error) { if err != nil { return nil, err } return &socketConn{ Conn: conn, }, nil } // wrapSocketConn wraps an existing net.Conn into *socketConn. func wrapSocketConn(conn net.Conn) *socketConn { // In case conn is already wrapped, // return it as-is and avoid double wrapping. if sc, ok := conn.(*socketConn); ok { return sc } return &socketConn{ Conn: conn, } } // isValid checks whether there's a valid connection. // // It's nil safe, and returns false if sc itself is nil, or if the underlying // connection is nil. // // It's the same as the previous implementation of TSocket.IsOpen and // TSSLSocket.IsOpen before we added connectivity check. func (sc *socketConn) isValid() bool { return sc != nil && sc.Conn != nil } // IsOpen checks whether the connection is open. // // It's nil safe, and returns false if sc itself is nil, or if the underlying // connection is nil. // // Otherwise, it tries to do a connectivity check and returns the result. // // It also has the side effect of resetting the previously set read deadline on // the socket. As a result, it shouldn't be called between setting read deadline // and doing actual read. func (sc *socketConn) IsOpen() bool { if !sc.isValid() { return false } return sc.checkConn() == nil } // Read implements io.Reader. // // On Windows, it behaves the same as the underlying net.Conn.Read. // // On non-Windows, it treats len(p) == 0 as a connectivity check instead of // readability check, which means instead of blocking until there's something to // read (readability check), or always return (0, nil) (the default behavior of // go's stdlib implementation on non-Windows), it never blocks, and will return // an error if the connection is lost. func (sc *socketConn) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, sc.read0() } return sc.Conn.Read(p) } socket_unix_conn.go000066400000000000000000000041631414226744000360530ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift// +build !windows /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "io" "syscall" "time" ) // We rely on this variable to be the zero time, // but define it as global variable to avoid repetitive allocations. // Please DO NOT mutate this variable in any way. var zeroTime time.Time func (sc *socketConn) read0() error { return sc.checkConn() } func (sc *socketConn) checkConn() error { syscallConn, ok := sc.Conn.(syscall.Conn) if !ok { // No way to check, return nil return nil } // The reading about to be done here is non-blocking so we don't really // need a read deadline. We just need to clear the previously set read // deadline, if any. sc.Conn.SetReadDeadline(zeroTime) rc, err := syscallConn.SyscallConn() if err != nil { return err } var n int if readErr := rc.Read(func(fd uintptr) bool { n, _, err = syscall.Recvfrom(int(fd), sc.buffer[:], syscall.MSG_PEEK|syscall.MSG_DONTWAIT) return true }); readErr != nil { return readErr } if n > 0 { // We got something, which means we are good return nil } if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EWOULDBLOCK) { // This means the connection is still open but we don't have // anything to read right now. return nil } if err != nil { return err } // At this point, it means the other side already closed the connection. return io.EOF } socket_windows_conn.go000066400000000000000000000021331414226744000365550ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift// +build windows /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift func (sc *socketConn) read0() error { // On windows, we fallback to the default behavior of reading 0 bytes. var p []byte _, err := sc.Conn.Read(p) return err } func (sc *socketConn) checkConn() error { // On windows, we always return nil for this check. return nil } ssl_server_socket.go000066400000000000000000000055471414226744000362510ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "crypto/tls" "net" "time" ) type TSSLServerSocket struct { listener net.Listener addr net.Addr clientTimeout time.Duration interrupted bool cfg *tls.Config } func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) { return NewTSSLServerSocketTimeout(listenAddr, cfg, 0) } func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) { if cfg.MinVersion == 0 { cfg.MinVersion = tls.VersionTLS10 } addr, err := net.ResolveTCPAddr("tcp", listenAddr) if err != nil { return nil, err } return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil } func (p *TSSLServerSocket) Listen() error { if p.IsListening() { return nil } l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg) if err != nil { return err } p.listener = l return nil } func (p *TSSLServerSocket) Accept() (TTransport, error) { if p.interrupted { return nil, errTransportInterrupted } if p.listener == nil { return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") } conn, err := p.listener.Accept() if err != nil { return nil, NewTTransportExceptionFromError(err) } return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil } // Checks whether the socket is listening. func (p *TSSLServerSocket) IsListening() bool { return p.listener != nil } // Connects the socket, creating a new socket object if necessary. func (p *TSSLServerSocket) Open() error { if p.IsListening() { return NewTTransportException(ALREADY_OPEN, "Server socket already open") } if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil { return err } else { p.listener = l } return nil } func (p *TSSLServerSocket) Addr() net.Addr { return p.addr } func (p *TSSLServerSocket) Close() error { defer func() { p.listener = nil }() if p.IsListening() { return p.listener.Close() } return nil } func (p *TSSLServerSocket) Interrupt() error { p.interrupted = true return nil } ssl_socket.go000066400000000000000000000156001414226744000346520ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "crypto/tls" "net" "time" ) type TSSLSocket struct { conn *socketConn // hostPort contains host:port (e.g. "asdf.com:12345"). The field is // only valid if addr is nil. hostPort string // addr is nil when hostPort is not "", and is only used when the // TSSLSocket is constructed from a net.Addr. addr net.Addr cfg *TConfiguration } // NewTSSLSocketConf creates a net.Conn-backed TTransport, given a host and port. // // Example: // // trans, err := thrift.NewTSSLSocketConf("localhost:9090", nil, &TConfiguration{ // ConnectTimeout: time.Second, // Use 0 for no timeout // SocketTimeout: time.Second, // Use 0 for no timeout // }) func NewTSSLSocketConf(hostPort string, conf *TConfiguration) (*TSSLSocket, error) { if cfg := conf.GetTLSConfig(); cfg != nil && cfg.MinVersion == 0 { cfg.MinVersion = tls.VersionTLS10 } return &TSSLSocket{ hostPort: hostPort, cfg: conf, }, nil } // Deprecated: Use NewTSSLSocketConf instead. func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) { return NewTSSLSocketConf(hostPort, &TConfiguration{ TLSConfig: cfg, noPropagation: true, }) } // Deprecated: Use NewTSSLSocketConf instead. func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) (*TSSLSocket, error) { return NewTSSLSocketConf(hostPort, &TConfiguration{ ConnectTimeout: connectTimeout, SocketTimeout: socketTimeout, TLSConfig: cfg, noPropagation: true, }) } // NewTSSLSocketFromAddrConf creates a TSSLSocket from a net.Addr. func NewTSSLSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSSLSocket { return &TSSLSocket{ addr: addr, cfg: conf, } } // Deprecated: Use NewTSSLSocketFromAddrConf instead. func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) *TSSLSocket { return NewTSSLSocketFromAddrConf(addr, &TConfiguration{ ConnectTimeout: connectTimeout, SocketTimeout: socketTimeout, TLSConfig: cfg, noPropagation: true, }) } // NewTSSLSocketFromConnConf creates a TSSLSocket from an existing net.Conn. func NewTSSLSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSSLSocket { return &TSSLSocket{ conn: wrapSocketConn(conn), addr: conn.RemoteAddr(), cfg: conf, } } // Deprecated: Use NewTSSLSocketFromConnConf instead. func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, socketTimeout time.Duration) *TSSLSocket { return NewTSSLSocketFromConnConf(conn, &TConfiguration{ SocketTimeout: socketTimeout, TLSConfig: cfg, noPropagation: true, }) } // SetTConfiguration implements TConfigurationSetter. // // It can be used to change connect and socket timeouts. func (p *TSSLSocket) SetTConfiguration(conf *TConfiguration) { p.cfg = conf } // Sets the connect timeout func (p *TSSLSocket) SetConnTimeout(timeout time.Duration) error { if p.cfg == nil { p.cfg = &TConfiguration{} } p.cfg.ConnectTimeout = timeout return nil } // Sets the socket timeout func (p *TSSLSocket) SetSocketTimeout(timeout time.Duration) error { if p.cfg == nil { p.cfg = &TConfiguration{} } p.cfg.SocketTimeout = timeout return nil } func (p *TSSLSocket) pushDeadline(read, write bool) { var t time.Time if timeout := p.cfg.GetSocketTimeout(); timeout > 0 { t = time.Now().Add(time.Duration(timeout)) } if read && write { p.conn.SetDeadline(t) } else if read { p.conn.SetReadDeadline(t) } else if write { p.conn.SetWriteDeadline(t) } } // Connects the socket, creating a new socket object if necessary. func (p *TSSLSocket) Open() error { var err error // If we have a hostname, we need to pass the hostname to tls.Dial for // certificate hostname checks. if p.hostPort != "" { if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer( &net.Dialer{ Timeout: p.cfg.GetConnectTimeout(), }, "tcp", p.hostPort, p.cfg.GetTLSConfig(), )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } else { if p.conn.isValid() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { return NewTTransportException(NOT_OPEN, "Cannot open nil address.") } if len(p.addr.Network()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") } if len(p.addr.String()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer( &net.Dialer{ Timeout: p.cfg.GetConnectTimeout(), }, p.addr.Network(), p.addr.String(), p.cfg.GetTLSConfig(), )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } return nil } // Retrieve the underlying net.Conn func (p *TSSLSocket) Conn() net.Conn { return p.conn } // Returns true if the connection is open func (p *TSSLSocket) IsOpen() bool { return p.conn.IsOpen() } // Closes the socket. func (p *TSSLSocket) Close() error { // Close the socket if p.conn != nil { err := p.conn.Close() if err != nil { return err } p.conn = nil } return nil } func (p *TSSLSocket) Read(buf []byte) (int, error) { if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between // p.pushDeadline and p.conn.Read could cause the deadline set inside // p.pushDeadline being reset, thus need to be avoided. n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSSLSocket) Write(buf []byte) (int, error) { if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) return p.conn.Write(buf) } func (p *TSSLSocket) Flush(ctx context.Context) error { return nil } func (p *TSSLSocket) Interrupt() error { if !p.conn.isValid() { return nil } return p.conn.Close() } func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the truth is, we just don't know unless framed is used } var _ TConfigurationSetter = (*TSSLSocket)(nil) transport.go000066400000000000000000000034001414226744000345300ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "context" "errors" "io" ) var errTransportInterrupted = errors.New("Transport Interrupted") type Flusher interface { Flush() (err error) } type ContextFlusher interface { Flush(ctx context.Context) (err error) } type ReadSizeProvider interface { RemainingBytes() (num_bytes uint64) } // Encapsulates the I/O layer type TTransport interface { io.ReadWriteCloser ContextFlusher ReadSizeProvider // Opens the transport for communication Open() error // Returns true if the transport is open IsOpen() bool } type stringWriter interface { WriteString(s string) (n int, err error) } // This is "enchanced" transport with extra capabilities. You need to use one of these // to construct protocol. // Notably, TSocket does not implement this interface, and it is always a mistake to use // TSocket directly in protocol. type TRichTransport interface { io.ReadWriter io.ByteReader io.ByteWriter stringWriter ContextFlusher ReadSizeProvider } transport_exception.go000066400000000000000000000053331414226744000366150ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "io" ) type timeoutable interface { Timeout() bool } // Thrift Transport exception type TTransportException interface { TException TypeId() int Err() error } const ( UNKNOWN_TRANSPORT_EXCEPTION = 0 NOT_OPEN = 1 ALREADY_OPEN = 2 TIMED_OUT = 3 END_OF_FILE = 4 ) type tTransportException struct { typeId int err error msg string } var _ TTransportException = (*tTransportException)(nil) func (tTransportException) TExceptionType() TExceptionType { return TExceptionTypeTransport } func (p *tTransportException) TypeId() int { return p.typeId } func (p *tTransportException) Error() string { return p.msg } func (p *tTransportException) Err() error { return p.err } func (p *tTransportException) Unwrap() error { return p.err } func (p *tTransportException) Timeout() bool { return p.typeId == TIMED_OUT } func NewTTransportException(t int, e string) TTransportException { return &tTransportException{ typeId: t, err: errors.New(e), msg: e, } } func NewTTransportExceptionFromError(e error) TTransportException { if e == nil { return nil } if t, ok := e.(TTransportException); ok { return t } te := &tTransportException{ typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e, msg: e.Error(), } if isTimeoutError(e) { te.typeId = TIMED_OUT return te } if errors.Is(e, io.EOF) { te.typeId = END_OF_FILE return te } return te } func prependTTransportException(prepend string, e TTransportException) TTransportException { return &tTransportException{ typeId: e.TypeId(), err: e, msg: prepend + e.Error(), } } // isTimeoutError returns true when err is an error caused by timeout. // // Note that this also includes TTransportException wrapped timeout errors. func isTimeoutError(err error) bool { var t timeoutable if errors.As(err, &t) { return t.Timeout() } return false } transport_factory.go000066400000000000000000000026301414226744000362630ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Factory class used to create wrapped instance of Transports. // This is used primarily in servers, which get Transports from // a ServerTransport and then may want to mutate them (i.e. create // a BufferedTransport from the underlying base transport) type TTransportFactory interface { GetTransport(trans TTransport) (TTransport, error) } type tTransportFactory struct{} // Return a wrapped instance of the base Transport. func (p *tTransportFactory) GetTransport(trans TTransport) (TTransport, error) { return trans, nil } func NewTTransportFactory() TTransportFactory { return &tTransportFactory{} } golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/type.go000066400000000000000000000027761414226744000335530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Type constants in the Thrift protocol type TType byte const ( STOP = 0 VOID = 1 BOOL = 2 BYTE = 3 I08 = 3 DOUBLE = 4 I16 = 6 I32 = 8 I64 = 10 STRING = 11 UTF7 = 11 STRUCT = 12 MAP = 13 SET = 14 LIST = 15 UTF8 = 16 UTF16 = 17 //BINARY = 18 wrong and unusued ) var typeNames = map[int]string{ STOP: "STOP", VOID: "VOID", BOOL: "BOOL", BYTE: "BYTE", DOUBLE: "DOUBLE", I16: "I16", I32: "I32", I64: "I64", STRING: "STRING", STRUCT: "STRUCT", MAP: "MAP", SET: "SET", LIST: "LIST", UTF8: "UTF8", UTF16: "UTF16", } func (p TType) String() string { if s, ok := typeNames[int(p)]; ok { return s } return "Unknown" } zlib_transport.go000066400000000000000000000074011414226744000355550ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/jaeger/internal/third_party/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "compress/zlib" "context" "io" ) // TZlibTransportFactory is a factory for TZlibTransport instances type TZlibTransportFactory struct { level int factory TTransportFactory } // TZlibTransport is a TTransport implementation that makes use of zlib compression. type TZlibTransport struct { reader io.ReadCloser transport TTransport writer *zlib.Writer } // GetTransport constructs a new instance of NewTZlibTransport func (p *TZlibTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if p.factory != nil { // wrap other factory var err error trans, err = p.factory.GetTransport(trans) if err != nil { return nil, err } } return NewTZlibTransport(trans, p.level) } // NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory func NewTZlibTransportFactory(level int) *TZlibTransportFactory { return &TZlibTransportFactory{level: level, factory: nil} } // NewTZlibTransportFactory constructs a new instance of TZlibTransportFactory // as a wrapper over existing transport factory func NewTZlibTransportFactoryWithFactory(level int, factory TTransportFactory) *TZlibTransportFactory { return &TZlibTransportFactory{level: level, factory: factory} } // NewTZlibTransport constructs a new instance of TZlibTransport func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) { w, err := zlib.NewWriterLevel(trans, level) if err != nil { return nil, err } return &TZlibTransport{ writer: w, transport: trans, }, nil } // Close closes the reader and writer (flushing any unwritten data) and closes // the underlying transport. func (z *TZlibTransport) Close() error { if z.reader != nil { if err := z.reader.Close(); err != nil { return err } } if err := z.writer.Close(); err != nil { return err } return z.transport.Close() } // Flush flushes the writer and its underlying transport. func (z *TZlibTransport) Flush(ctx context.Context) error { if err := z.writer.Flush(); err != nil { return err } return z.transport.Flush(ctx) } // IsOpen returns true if the transport is open func (z *TZlibTransport) IsOpen() bool { return z.transport.IsOpen() } // Open opens the transport for communication func (z *TZlibTransport) Open() error { return z.transport.Open() } func (z *TZlibTransport) Read(p []byte) (int, error) { if z.reader == nil { r, err := zlib.NewReader(z.transport) if err != nil { return 0, NewTTransportExceptionFromError(err) } z.reader = r } return z.reader.Read(p) } // RemainingBytes returns the size in bytes of the data that is still to be // read. func (z *TZlibTransport) RemainingBytes() uint64 { return z.transport.RemainingBytes() } func (z *TZlibTransport) Write(p []byte) (int, error) { return z.writer.Write(p) } // SetTConfiguration implements TConfigurationSetter for propagation. func (z *TZlibTransport) SetTConfiguration(conf *TConfiguration) { PropagateTConfiguration(z.transport, conf) } var _ TConfigurationSetter = (*TZlibTransport)(nil) golang-opentelemetry-otel-1.1.0/exporters/jaeger/jaeger.go000066400000000000000000000224751414226744000237050ustar00rootroot00000000000000// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" import ( "context" "encoding/binary" "encoding/json" "fmt" "sync" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) const ( keyInstrumentationLibraryName = "otel.library.name" keyInstrumentationLibraryVersion = "otel.library.version" keyError = "error" keySpanKind = "span.kind" keyStatusCode = "otel.status_code" keyStatusMessage = "otel.status_description" keyDroppedAttributeCount = "otel.event.dropped_attributes_count" keyEventName = "event" ) // New returns an OTel Exporter implementation that exports the collected // spans to Jaeger. func New(endpointOption EndpointOption) (*Exporter, error) { uploader, err := endpointOption.newBatchUploader() if err != nil { return nil, err } // Fetch default service.name from default resource for backup var defaultServiceName string defaultResource := resource.Default() if value, exists := defaultResource.Set().Value(semconv.ServiceNameKey); exists { defaultServiceName = value.AsString() } if defaultServiceName == "" { return nil, fmt.Errorf("failed to get service name from default resource") } stopCh := make(chan struct{}) e := &Exporter{ uploader: uploader, stopCh: stopCh, defaultServiceName: defaultServiceName, } return e, nil } // Exporter exports OpenTelemetry spans to a Jaeger agent or collector. type Exporter struct { uploader batchUploader stopOnce sync.Once stopCh chan struct{} defaultServiceName string } var _ sdktrace.SpanExporter = (*Exporter)(nil) // ExportSpans transforms and exports OpenTelemetry spans to Jaeger. func (e *Exporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { // Return fast if context is already canceled or Exporter shutdown. select { case <-ctx.Done(): return ctx.Err() case <-e.stopCh: return nil default: } // Cancel export if Exporter is shutdown. var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) defer cancel() go func(ctx context.Context, cancel context.CancelFunc) { select { case <-ctx.Done(): case <-e.stopCh: cancel() } }(ctx, cancel) for _, batch := range jaegerBatchList(spans, e.defaultServiceName) { if err := e.uploader.upload(ctx, batch); err != nil { return err } } return nil } // Shutdown stops the Exporter. This will close all connections and release // all resources held by the Exporter. func (e *Exporter) Shutdown(ctx context.Context) error { // Stop any active and subsequent exports. e.stopOnce.Do(func() { close(e.stopCh) }) select { case <-ctx.Done(): return ctx.Err() default: } return e.uploader.shutdown(ctx) } func spanToThrift(ss sdktrace.ReadOnlySpan) *gen.Span { attr := ss.Attributes() tags := make([]*gen.Tag, 0, len(attr)) for _, kv := range attr { tag := keyValueToTag(kv) if tag != nil { tags = append(tags, tag) } } if il := ss.InstrumentationLibrary(); il.Name != "" { tags = append(tags, getStringTag(keyInstrumentationLibraryName, il.Name)) if il.Version != "" { tags = append(tags, getStringTag(keyInstrumentationLibraryVersion, il.Version)) } } if ss.SpanKind() != trace.SpanKindInternal { tags = append(tags, getStringTag(keySpanKind, ss.SpanKind().String()), ) } if ss.Status().Code != codes.Unset { tags = append(tags, getInt64Tag(keyStatusCode, int64(ss.Status().Code))) if ss.Status().Description != "" { tags = append(tags, getStringTag(keyStatusMessage, ss.Status().Description)) } if ss.Status().Code == codes.Error { tags = append(tags, getBoolTag(keyError, true)) } } var logs []*gen.Log for _, a := range ss.Events() { nTags := len(a.Attributes) if a.Name != "" { nTags++ } if a.DroppedAttributeCount != 0 { nTags++ } fields := make([]*gen.Tag, 0, nTags) if a.Name != "" { // If an event contains an attribute with the same key, it needs // to be given precedence and overwrite this. fields = append(fields, getStringTag(keyEventName, a.Name)) } for _, kv := range a.Attributes { tag := keyValueToTag(kv) if tag != nil { fields = append(fields, tag) } } if a.DroppedAttributeCount != 0 { fields = append(fields, getInt64Tag(keyDroppedAttributeCount, int64(a.DroppedAttributeCount))) } logs = append(logs, &gen.Log{ Timestamp: a.Time.UnixNano() / 1000, Fields: fields, }) } var refs []*gen.SpanRef for _, link := range ss.Links() { tid := link.SpanContext.TraceID() sid := link.SpanContext.SpanID() refs = append(refs, &gen.SpanRef{ TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])), TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])), SpanId: int64(binary.BigEndian.Uint64(sid[:])), RefType: gen.SpanRefType_FOLLOWS_FROM, }) } tid := ss.SpanContext().TraceID() sid := ss.SpanContext().SpanID() psid := ss.Parent().SpanID() return &gen.Span{ TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])), TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])), SpanId: int64(binary.BigEndian.Uint64(sid[:])), ParentSpanId: int64(binary.BigEndian.Uint64(psid[:])), OperationName: ss.Name(), // TODO: if span kind is added then add prefix "Sent"/"Recv" Flags: int32(ss.SpanContext().TraceFlags()), StartTime: ss.StartTime().UnixNano() / 1000, Duration: ss.EndTime().Sub(ss.StartTime()).Nanoseconds() / 1000, Tags: tags, Logs: logs, References: refs, } } func keyValueToTag(keyValue attribute.KeyValue) *gen.Tag { var tag *gen.Tag switch keyValue.Value.Type() { case attribute.STRING: s := keyValue.Value.AsString() tag = &gen.Tag{ Key: string(keyValue.Key), VStr: &s, VType: gen.TagType_STRING, } case attribute.BOOL: b := keyValue.Value.AsBool() tag = &gen.Tag{ Key: string(keyValue.Key), VBool: &b, VType: gen.TagType_BOOL, } case attribute.INT64: i := keyValue.Value.AsInt64() tag = &gen.Tag{ Key: string(keyValue.Key), VLong: &i, VType: gen.TagType_LONG, } case attribute.FLOAT64: f := keyValue.Value.AsFloat64() tag = &gen.Tag{ Key: string(keyValue.Key), VDouble: &f, VType: gen.TagType_DOUBLE, } case attribute.BOOLSLICE, attribute.INT64SLICE, attribute.FLOAT64SLICE, attribute.STRINGSLICE: json, _ := json.Marshal(keyValue.Value.AsInterface()) a := (string)(json) tag = &gen.Tag{ Key: string(keyValue.Key), VStr: &a, VType: gen.TagType_STRING, } } return tag } func getInt64Tag(k string, i int64) *gen.Tag { return &gen.Tag{ Key: k, VLong: &i, VType: gen.TagType_LONG, } } func getStringTag(k, s string) *gen.Tag { return &gen.Tag{ Key: k, VStr: &s, VType: gen.TagType_STRING, } } func getBoolTag(k string, b bool) *gen.Tag { return &gen.Tag{ Key: k, VBool: &b, VType: gen.TagType_BOOL, } } // jaegerBatchList transforms a slice of spans into a slice of jaeger Batch. func jaegerBatchList(ssl []sdktrace.ReadOnlySpan, defaultServiceName string) []*gen.Batch { if len(ssl) == 0 { return nil } batchDict := make(map[attribute.Distinct]*gen.Batch) for _, ss := range ssl { if ss == nil { continue } resourceKey := ss.Resource().Equivalent() batch, bOK := batchDict[resourceKey] if !bOK { batch = &gen.Batch{ Process: process(ss.Resource(), defaultServiceName), Spans: []*gen.Span{}, } } batch.Spans = append(batch.Spans, spanToThrift(ss)) batchDict[resourceKey] = batch } // Transform the categorized map into a slice batchList := make([]*gen.Batch, 0, len(batchDict)) for _, batch := range batchDict { batchList = append(batchList, batch) } return batchList } // process transforms an OTel Resource into a jaeger Process. func process(res *resource.Resource, defaultServiceName string) *gen.Process { var process gen.Process var serviceName attribute.KeyValue if res != nil { for iter := res.Iter(); iter.Next(); { if iter.Attribute().Key == semconv.ServiceNameKey { serviceName = iter.Attribute() // Don't convert service.name into tag. continue } if tag := keyValueToTag(iter.Attribute()); tag != nil { process.Tags = append(process.Tags, tag) } } } // If no service.name is contained in a Span's Resource, // that field MUST be populated from the default Resource. if serviceName.Value.AsString() == "" { serviceName = semconv.ServiceNameKey.String(defaultServiceName) } process.ServiceName = serviceName.Value.AsString() return &process } golang-opentelemetry-otel-1.1.0/exporters/jaeger/jaeger_benchmark_test.go000066400000000000000000000062571414226744000267560ustar00rootroot00000000000000// 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 jaeger import ( "context" "fmt" "testing" "time" "go.opentelemetry.io/otel/sdk/instrumentation" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) var ( traceID trace.TraceID spanID trace.SpanID spanContext trace.SpanContext instrLibName = "benchmark.tests" ) func init() { var err error traceID, err = trace.TraceIDFromHex("0102030405060708090a0b0c0d0e0f10") if err != nil { panic(err) } spanID, err = trace.SpanIDFromHex("0102030405060708") if err != nil { panic(err) } spanContext = trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }) } func spans(n int) []tracesdk.ReadOnlySpan { now := time.Now() s := make(tracetest.SpanStubs, n) for i := 0; i < n; i++ { name := fmt.Sprintf("span %d", i) s[i] = tracetest.SpanStub{ SpanContext: spanContext, Name: name, StartTime: now, EndTime: now, SpanKind: trace.SpanKindClient, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, }, } } return s.Snapshots() } func benchmarkExportSpans(b *testing.B, o EndpointOption, i int) { ctx := context.Background() s := spans(i) exp, err := New(o) if err != nil { b.Fatal(err) } b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { if err := exp.ExportSpans(ctx, s); err != nil { b.Error(err) } } } func benchmarkCollector(b *testing.B, i int) { benchmarkExportSpans(b, withTestCollectorEndpoint(), i) } func benchmarkAgent(b *testing.B, i int) { benchmarkExportSpans(b, WithAgentEndpoint(), i) } func BenchmarkCollectorExportSpans1(b *testing.B) { benchmarkCollector(b, 1) } func BenchmarkCollectorExportSpans10(b *testing.B) { benchmarkCollector(b, 10) } func BenchmarkCollectorExportSpans100(b *testing.B) { benchmarkCollector(b, 100) } func BenchmarkCollectorExportSpans1000(b *testing.B) { benchmarkCollector(b, 1000) } func BenchmarkCollectorExportSpans10000(b *testing.B) { benchmarkCollector(b, 10000) } func BenchmarkAgentExportSpans1(b *testing.B) { benchmarkAgent(b, 1) } func BenchmarkAgentExportSpans10(b *testing.B) { benchmarkAgent(b, 10) } func BenchmarkAgentExportSpans100(b *testing.B) { benchmarkAgent(b, 100) } /* * BUG: These tests are not possible currently because the thrift payload size * does not fit in a UDP packet with the default size (65000) and will return * an error. func BenchmarkAgentExportSpans1000(b *testing.B) { benchmarkAgent(b, 1000) } func BenchmarkAgentExportSpans10000(b *testing.B) { benchmarkAgent(b, 10000) } */ golang-opentelemetry-otel-1.1.0/exporters/jaeger/jaeger_test.go000066400000000000000000000464561414226744000247510ustar00rootroot00000000000000// 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 jaeger import ( "context" "encoding/binary" "fmt" "os" "sort" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) func TestNewRawExporter(t *testing.T) { testCases := []struct { name string endpoint EndpointOption }{ { name: "default exporter with collector endpoint", endpoint: WithCollectorEndpoint(), }, { name: "default exporter with agent endpoint", endpoint: WithAgentEndpoint(), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { _, err := New(tc.endpoint) assert.NoError(t, err) }) } } func TestNewRawExporterUseEnvVarIfOptionUnset(t *testing.T) { // Record and restore env envStore := ottest.NewEnvStore() envStore.Record(envEndpoint) defer func() { require.NoError(t, envStore.Restore()) }() // If the user sets the environment variable OTEL_EXPORTER_JAEGER_ENDPOINT, endpoint will always get a value. require.NoError(t, os.Unsetenv(envEndpoint)) _, err := New( WithCollectorEndpoint(), ) assert.NoError(t, err) } type testCollectorEndpoint struct { batchesUploaded []*gen.Batch } func (c *testCollectorEndpoint) shutdown(context.Context) error { return nil } func (c *testCollectorEndpoint) upload(_ context.Context, batch *gen.Batch) error { c.batchesUploaded = append(c.batchesUploaded, batch) return nil } var _ batchUploader = (*testCollectorEndpoint)(nil) func withTestCollectorEndpoint() EndpointOption { return endpointOptionFunc(func() (batchUploader, error) { return &testCollectorEndpoint{}, nil }) } func withTestCollectorEndpointInjected(ce *testCollectorEndpoint) EndpointOption { return endpointOptionFunc(func() (batchUploader, error) { return ce, nil }) } func TestExporterExportSpan(t *testing.T) { const ( serviceName = "test-service" tagKey = "key" tagVal = "val" ) testCollector := &testCollectorEndpoint{} exp, err := New(withTestCollectorEndpointInjected(testCollector)) require.NoError(t, err) tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), sdktrace.WithResource(resource.NewSchemaless( semconv.ServiceNameKey.String(serviceName), attribute.String(tagKey, tagVal), )), ) tracer := tp.Tracer("test-tracer") ctx := context.Background() for i := 0; i < 3; i++ { _, span := tracer.Start(ctx, fmt.Sprintf("test-span-%d", i)) span.End() assert.True(t, span.SpanContext().IsValid()) } require.NoError(t, tp.Shutdown(ctx)) batchesUploaded := testCollector.batchesUploaded require.Len(t, batchesUploaded, 1) uploadedBatch := batchesUploaded[0] assert.Equal(t, serviceName, uploadedBatch.GetProcess().GetServiceName()) assert.Len(t, uploadedBatch.GetSpans(), 3) require.Len(t, uploadedBatch.GetProcess().GetTags(), 1) assert.Equal(t, tagKey, uploadedBatch.GetProcess().GetTags()[0].GetKey()) assert.Equal(t, tagVal, uploadedBatch.GetProcess().GetTags()[0].GetVStr()) } func Test_spanSnapshotToThrift(t *testing.T) { now := time.Now() traceID, _ := trace.TraceIDFromHex("0102030405060708090a0b0c0d0e0f10") spanID, _ := trace.SpanIDFromHex("0102030405060708") parentSpanID, _ := trace.SpanIDFromHex("0807060504030201") linkTraceID, _ := trace.TraceIDFromHex("0102030405060709090a0b0c0d0e0f11") linkSpanID, _ := trace.SpanIDFromHex("0102030405060709") eventNameValue := "event-test" eventDropped := int64(10) keyValue := "value" statusCodeValue := int64(1) doubleValue := 123.456 intValue := int64(123) boolTrue := true arrValue := "[0,1,2,3]" statusMessage := "this is a problem" spanKind := "client" rv1 := "rv11" rv2 := int64(5) instrLibName := "instrumentation-library" instrLibVersion := "semver:1.0.0" tests := []struct { name string data tracetest.SpanStub want *gen.Span }{ { name: "no status description", data: tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }), Name: "/foo", StartTime: now, EndTime: now, Status: sdktrace.Status{Code: codes.Error}, SpanKind: trace.SpanKindClient, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, Version: instrLibVersion, }, }, want: &gen.Span{ TraceIdLow: 651345242494996240, TraceIdHigh: 72623859790382856, SpanId: 72623859790382856, OperationName: "/foo", StartTime: now.UnixNano() / 1000, Duration: 0, Tags: []*gen.Tag{ {Key: keyError, VType: gen.TagType_BOOL, VBool: &boolTrue}, {Key: keyInstrumentationLibraryName, VType: gen.TagType_STRING, VStr: &instrLibName}, {Key: keyInstrumentationLibraryVersion, VType: gen.TagType_STRING, VStr: &instrLibVersion}, {Key: keyStatusCode, VType: gen.TagType_LONG, VLong: &statusCodeValue}, // Should not have a status message because it was unset {Key: keySpanKind, VType: gen.TagType_STRING, VStr: &spanKind}, }, }, }, { name: "no parent", data: tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }), Name: "/foo", StartTime: now, EndTime: now, Links: []sdktrace.Link{ { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: linkTraceID, SpanID: linkSpanID, }), }, }, Attributes: []attribute.KeyValue{ attribute.String("key", keyValue), attribute.Float64("double", doubleValue), attribute.Int64("int", intValue), }, Events: []sdktrace.Event{ { Name: eventNameValue, Attributes: []attribute.KeyValue{attribute.String("k1", keyValue)}, DroppedAttributeCount: int(eventDropped), Time: now, }, }, Status: sdktrace.Status{ Code: codes.Error, Description: statusMessage, }, SpanKind: trace.SpanKindClient, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, Version: instrLibVersion, }, }, want: &gen.Span{ TraceIdLow: 651345242494996240, TraceIdHigh: 72623859790382856, SpanId: 72623859790382856, OperationName: "/foo", StartTime: now.UnixNano() / 1000, Duration: 0, Tags: []*gen.Tag{ {Key: "double", VType: gen.TagType_DOUBLE, VDouble: &doubleValue}, {Key: "key", VType: gen.TagType_STRING, VStr: &keyValue}, {Key: "int", VType: gen.TagType_LONG, VLong: &intValue}, {Key: keyError, VType: gen.TagType_BOOL, VBool: &boolTrue}, {Key: keyInstrumentationLibraryName, VType: gen.TagType_STRING, VStr: &instrLibName}, {Key: keyInstrumentationLibraryVersion, VType: gen.TagType_STRING, VStr: &instrLibVersion}, {Key: keyStatusCode, VType: gen.TagType_LONG, VLong: &statusCodeValue}, {Key: keyStatusMessage, VType: gen.TagType_STRING, VStr: &statusMessage}, {Key: keySpanKind, VType: gen.TagType_STRING, VStr: &spanKind}, }, References: []*gen.SpanRef{ { RefType: gen.SpanRefType_FOLLOWS_FROM, TraceIdHigh: int64(binary.BigEndian.Uint64(linkTraceID[0:8])), TraceIdLow: int64(binary.BigEndian.Uint64(linkTraceID[8:16])), SpanId: int64(binary.BigEndian.Uint64(linkSpanID[:])), }, }, Logs: []*gen.Log{ { Timestamp: now.UnixNano() / 1000, Fields: []*gen.Tag{ { Key: keyEventName, VStr: &eventNameValue, VType: gen.TagType_STRING, }, { Key: "k1", VStr: &keyValue, VType: gen.TagType_STRING, }, { Key: keyDroppedAttributeCount, VLong: &eventDropped, VType: gen.TagType_LONG, }, }, }, }, }, }, { name: "with parent", data: tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: parentSpanID, }), Links: []sdktrace.Link{ { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: linkTraceID, SpanID: linkSpanID, }), }, }, Name: "/foo", StartTime: now, EndTime: now, Attributes: []attribute.KeyValue{ attribute.IntSlice("arr", []int{0, 1, 2, 3}), }, Status: sdktrace.Status{ Code: codes.Unset, Description: statusMessage, }, SpanKind: trace.SpanKindInternal, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, Version: instrLibVersion, }, }, want: &gen.Span{ TraceIdLow: 651345242494996240, TraceIdHigh: 72623859790382856, SpanId: 72623859790382856, ParentSpanId: 578437695752307201, OperationName: "/foo", StartTime: now.UnixNano() / 1000, Duration: 0, Tags: []*gen.Tag{ // status code, message and span kind should NOT be populated {Key: "arr", VType: gen.TagType_STRING, VStr: &arrValue}, {Key: keyInstrumentationLibraryName, VType: gen.TagType_STRING, VStr: &instrLibName}, {Key: keyInstrumentationLibraryVersion, VType: gen.TagType_STRING, VStr: &instrLibVersion}, }, References: []*gen.SpanRef{ { RefType: gen.SpanRefType_FOLLOWS_FROM, TraceIdHigh: int64(binary.BigEndian.Uint64(linkTraceID[0:8])), TraceIdLow: int64(binary.BigEndian.Uint64(linkTraceID[8:16])), SpanId: int64(binary.BigEndian.Uint64(linkSpanID[:])), }, }, }, }, { name: "resources do not affect the tags", data: tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: parentSpanID, }), Name: "/foo", StartTime: now, EndTime: now, Resource: resource.NewSchemaless( attribute.String("rk1", rv1), attribute.Int64("rk2", rv2), semconv.ServiceNameKey.String("service name"), ), Status: sdktrace.Status{ Code: codes.Unset, Description: statusMessage, }, SpanKind: trace.SpanKindInternal, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, Version: instrLibVersion, }, }, want: &gen.Span{ TraceIdLow: 651345242494996240, TraceIdHigh: 72623859790382856, SpanId: 72623859790382856, ParentSpanId: 578437695752307201, OperationName: "/foo", StartTime: now.UnixNano() / 1000, Duration: 0, Tags: []*gen.Tag{ {Key: keyInstrumentationLibraryName, VType: gen.TagType_STRING, VStr: &instrLibName}, {Key: keyInstrumentationLibraryVersion, VType: gen.TagType_STRING, VStr: &instrLibVersion}, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := spanToThrift(tt.data.Snapshot()) sort.Slice(got.Tags, func(i, j int) bool { return got.Tags[i].Key < got.Tags[j].Key }) sort.Slice(tt.want.Tags, func(i, j int) bool { return tt.want.Tags[i].Key < tt.want.Tags[j].Key }) if diff := cmp.Diff(got, tt.want); diff != "" { t.Errorf("Diff%v", diff) } }) } } func TestExporterShutdownHonorsCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() e, err := New(withTestCollectorEndpoint()) require.NoError(t, err) assert.EqualError(t, e.Shutdown(ctx), context.Canceled.Error()) } func TestExporterShutdownHonorsTimeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) <-ctx.Done() e, err := New(withTestCollectorEndpoint()) require.NoError(t, err) assert.EqualError(t, e.Shutdown(ctx), context.DeadlineExceeded.Error()) cancel() } func TestErrorOnExportShutdownExporter(t *testing.T) { e, err := New(withTestCollectorEndpoint()) require.NoError(t, err) assert.NoError(t, e.Shutdown(context.Background())) assert.NoError(t, e.ExportSpans(context.Background(), nil)) } func TestExporterExportSpansHonorsCancel(t *testing.T) { e, err := New(withTestCollectorEndpoint()) require.NoError(t, err) now := time.Now() ss := tracetest.SpanStubs{ { Name: "s1", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }, { Name: "s2", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r2").String("v2"), ), StartTime: now, EndTime: now, }, } ctx, cancel := context.WithCancel(context.Background()) cancel() assert.EqualError(t, e.ExportSpans(ctx, ss.Snapshots()), context.Canceled.Error()) } func TestExporterExportSpansHonorsTimeout(t *testing.T) { e, err := New(withTestCollectorEndpoint()) require.NoError(t, err) now := time.Now() ss := tracetest.SpanStubs{ { Name: "s1", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }, { Name: "s2", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r2").String("v2"), ), StartTime: now, EndTime: now, }, } ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) defer cancel() <-ctx.Done() assert.EqualError(t, e.ExportSpans(ctx, ss.Snapshots()), context.DeadlineExceeded.Error()) } func TestJaegerBatchList(t *testing.T) { newString := func(value string) *string { return &value } spanKind := "unspecified" now := time.Now() testCases := []struct { name string roSpans []sdktrace.ReadOnlySpan defaultServiceName string expectedBatchList []*gen.Batch }{ { name: "no span shots", roSpans: nil, expectedBatchList: nil, }, { name: "span's snapshot contains nil span", roSpans: []sdktrace.ReadOnlySpan{ tracetest.SpanStub{ Name: "s1", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }.Snapshot(), nil, }, expectedBatchList: []*gen.Batch{ { Process: &gen.Process{ ServiceName: "name", Tags: []*gen.Tag{ {Key: "r1", VType: gen.TagType_STRING, VStr: newString("v1")}, }, }, Spans: []*gen.Span{ { OperationName: "s1", Tags: []*gen.Tag{ {Key: keySpanKind, VType: gen.TagType_STRING, VStr: &spanKind}, }, StartTime: now.UnixNano() / 1000, }, }, }, }, }, { name: "merge spans that have the same resources", roSpans: tracetest.SpanStubs{ { Name: "s1", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }, { Name: "s2", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }, { Name: "s3", Resource: resource.NewSchemaless( semconv.ServiceNameKey.String("name"), attribute.Key("r2").String("v2"), ), StartTime: now, EndTime: now, }, }.Snapshots(), expectedBatchList: []*gen.Batch{ { Process: &gen.Process{ ServiceName: "name", Tags: []*gen.Tag{ {Key: "r1", VType: gen.TagType_STRING, VStr: newString("v1")}, }, }, Spans: []*gen.Span{ { OperationName: "s1", Tags: []*gen.Tag{ {Key: "span.kind", VType: gen.TagType_STRING, VStr: &spanKind}, }, StartTime: now.UnixNano() / 1000, }, { OperationName: "s2", Tags: []*gen.Tag{ {Key: "span.kind", VType: gen.TagType_STRING, VStr: &spanKind}, }, StartTime: now.UnixNano() / 1000, }, }, }, { Process: &gen.Process{ ServiceName: "name", Tags: []*gen.Tag{ {Key: "r2", VType: gen.TagType_STRING, VStr: newString("v2")}, }, }, Spans: []*gen.Span{ { OperationName: "s3", Tags: []*gen.Tag{ {Key: "span.kind", VType: gen.TagType_STRING, VStr: &spanKind}, }, StartTime: now.UnixNano() / 1000, }, }, }, }, }, { name: "no service name in spans", roSpans: tracetest.SpanStubs{ { Name: "s1", Resource: resource.NewSchemaless( attribute.Key("r1").String("v1"), ), StartTime: now, EndTime: now, }, }.Snapshots(), defaultServiceName: "default service name", expectedBatchList: []*gen.Batch{ { Process: &gen.Process{ ServiceName: "default service name", Tags: []*gen.Tag{ {Key: "r1", VType: gen.TagType_STRING, VStr: newString("v1")}, }, }, Spans: []*gen.Span{ { OperationName: "s1", Tags: []*gen.Tag{ {Key: "span.kind", VType: gen.TagType_STRING, VStr: &spanKind}, }, StartTime: now.UnixNano() / 1000, }, }, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { batchList := jaegerBatchList(tc.roSpans, tc.defaultServiceName) assert.ElementsMatch(t, tc.expectedBatchList, batchList) }) } } func TestProcess(t *testing.T) { v1 := "v1" testCases := []struct { name string res *resource.Resource defaultServiceName string expectedProcess *gen.Process }{ { name: "resources contain service name", res: resource.NewSchemaless( semconv.ServiceNameKey.String("service name"), attribute.Key("r1").String("v1"), ), defaultServiceName: "default service name", expectedProcess: &gen.Process{ ServiceName: "service name", Tags: []*gen.Tag{ {Key: "r1", VType: gen.TagType_STRING, VStr: &v1}, }, }, }, { name: "resources don't have service name", res: resource.NewSchemaless(attribute.Key("r1").String("v1")), defaultServiceName: "default service name", expectedProcess: &gen.Process{ ServiceName: "default service name", Tags: []*gen.Tag{ {Key: "r1", VType: gen.TagType_STRING, VStr: &v1}, }, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { pro := process(tc.res, tc.defaultServiceName) assert.Equal(t, tc.expectedProcess, pro) }) } } golang-opentelemetry-otel-1.1.0/exporters/jaeger/reconnecting_udp_client.go000066400000000000000000000131761414226744000273320ustar00rootroot00000000000000// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" import ( "fmt" "log" "net" "sync" "sync/atomic" "time" ) // reconnectingUDPConn is an implementation of udpConn that resolves hostPort every resolveTimeout, if the resolved address is // different than the current conn then the new address is dialed and the conn is swapped. type reconnectingUDPConn struct { // `sync/atomic` expects the first word in an allocated struct to be 64-bit // aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details. bufferBytes int64 hostPort string resolveFunc resolveFunc dialFunc dialFunc logger *log.Logger connMtx sync.RWMutex conn *net.UDPConn destAddr *net.UDPAddr closeChan chan struct{} } type resolveFunc func(network string, hostPort string) (*net.UDPAddr, error) type dialFunc func(network string, laddr, raddr *net.UDPAddr) (*net.UDPConn, error) // newReconnectingUDPConn returns a new udpConn that resolves hostPort every resolveTimeout, if the resolved address is // different than the current conn then the new address is dialed and the conn is swapped. func newReconnectingUDPConn(hostPort string, bufferBytes int, resolveTimeout time.Duration, resolveFunc resolveFunc, dialFunc dialFunc, logger *log.Logger) (*reconnectingUDPConn, error) { conn := &reconnectingUDPConn{ hostPort: hostPort, resolveFunc: resolveFunc, dialFunc: dialFunc, logger: logger, closeChan: make(chan struct{}), bufferBytes: int64(bufferBytes), } if err := conn.attemptResolveAndDial(); err != nil { conn.logf("failed resolving destination address on connection startup, with err: %q. retrying in %s", err.Error(), resolveTimeout) } go conn.reconnectLoop(resolveTimeout) return conn, nil } func (c *reconnectingUDPConn) logf(format string, args ...interface{}) { if c.logger != nil { c.logger.Printf(format, args...) } } func (c *reconnectingUDPConn) reconnectLoop(resolveTimeout time.Duration) { ticker := time.NewTicker(resolveTimeout) defer ticker.Stop() for { select { case <-c.closeChan: return case <-ticker.C: if err := c.attemptResolveAndDial(); err != nil { c.logf("%s", err.Error()) } } } } func (c *reconnectingUDPConn) attemptResolveAndDial() error { newAddr, err := c.resolveFunc("udp", c.hostPort) if err != nil { return fmt.Errorf("failed to resolve new addr for host %q, with err: %w", c.hostPort, err) } c.connMtx.RLock() curAddr := c.destAddr c.connMtx.RUnlock() // dont attempt dial if an addr was successfully dialed previously and, resolved addr is the same as current conn if curAddr != nil && newAddr.String() == curAddr.String() { return nil } if err := c.attemptDialNewAddr(newAddr); err != nil { return fmt.Errorf("failed to dial newly resolved addr '%s', with err: %w", newAddr, err) } return nil } func (c *reconnectingUDPConn) attemptDialNewAddr(newAddr *net.UDPAddr) error { connUDP, err := c.dialFunc(newAddr.Network(), nil, newAddr) if err != nil { return err } if bufferBytes := int(atomic.LoadInt64(&c.bufferBytes)); bufferBytes != 0 { if err = connUDP.SetWriteBuffer(bufferBytes); err != nil { return err } } c.connMtx.Lock() c.destAddr = newAddr // store prev to close later prevConn := c.conn c.conn = connUDP c.connMtx.Unlock() if prevConn != nil { return prevConn.Close() } return nil } // Write calls net.udpConn.Write, if it fails an attempt is made to connect to a new addr, if that succeeds the write is retried before returning func (c *reconnectingUDPConn) Write(b []byte) (int, error) { var bytesWritten int var err error c.connMtx.RLock() conn := c.conn c.connMtx.RUnlock() if conn == nil { // if connection is not initialized indicate this with err in order to hook into retry logic err = fmt.Errorf("UDP connection not yet initialized, an address has not been resolved") } else { bytesWritten, err = conn.Write(b) } if err == nil { return bytesWritten, nil } // attempt to resolve and dial new address in case that's the problem, if resolve and dial succeeds, try write again if reconnErr := c.attemptResolveAndDial(); reconnErr == nil { c.connMtx.RLock() conn := c.conn c.connMtx.RUnlock() return conn.Write(b) } // return original error if reconn fails return bytesWritten, err } // Close stops the reconnectLoop, then closes the connection via net.udpConn 's implementation func (c *reconnectingUDPConn) Close() error { close(c.closeChan) // acquire rw lock before closing conn to ensure calls to Write drain c.connMtx.Lock() defer c.connMtx.Unlock() if c.conn != nil { return c.conn.Close() } return nil } // SetWriteBuffer defers to the net.udpConn SetWriteBuffer implementation wrapped with a RLock. if no conn is currently held // and SetWriteBuffer is called store bufferBytes to be set for new conns func (c *reconnectingUDPConn) SetWriteBuffer(bytes int) error { var err error c.connMtx.RLock() conn := c.conn c.connMtx.RUnlock() if conn != nil { err = c.conn.SetWriteBuffer(bytes) } if err == nil { atomic.StoreInt64(&c.bufferBytes, int64(bytes)) } return err } golang-opentelemetry-otel-1.1.0/exporters/jaeger/reconnecting_udp_client_test.go000066400000000000000000000303531414226744000303650ustar00rootroot00000000000000// 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 jaeger import ( "context" "fmt" "math/rand" "net" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) type mockResolver struct { mock.Mock } func (m *mockResolver) ResolveUDPAddr(network string, hostPort string) (*net.UDPAddr, error) { args := m.Called(network, hostPort) a0 := args.Get(0) if a0 == nil { return (*net.UDPAddr)(nil), args.Error(1) } return a0.(*net.UDPAddr), args.Error(1) } type mockDialer struct { mock.Mock } func (m *mockDialer) DialUDP(network string, laddr, raddr *net.UDPAddr) (*net.UDPConn, error) { args := m.Called(network, laddr, raddr) a0 := args.Get(0) if a0 == nil { return (*net.UDPConn)(nil), args.Error(1) } return a0.(*net.UDPConn), args.Error(1) } func newUDPListener() (net.PacketConn, error) { return net.ListenPacket("udp", "127.0.0.1:0") } func newUDPConn() (net.PacketConn, *net.UDPConn, error) { mockServer, err := newUDPListener() if err != nil { return nil, nil, err } addr, err := net.ResolveUDPAddr("udp", mockServer.LocalAddr().String()) if err != nil { mockServer.Close() return nil, nil, err } conn, err := net.DialUDP("udp", nil, addr) if err != nil { mockServer.Close() return nil, nil, err } return mockServer, conn, nil } func assertConnWritable(t *testing.T, conn udpConn, serverConn net.PacketConn) { expectedString := "yo this is a test" _, err := conn.Write([]byte(expectedString)) require.NoError(t, err) var buf = make([]byte, len(expectedString)) err = serverConn.SetReadDeadline(time.Now().Add(time.Second)) require.NoError(t, err) _, _, err = serverConn.ReadFrom(buf) require.NoError(t, err) require.Equal(t, []byte(expectedString), buf) } func waitForCallWithTimeout(call *mock.Call) bool { called := make(chan struct{}) call.Run(func(args mock.Arguments) { if !isChannelClosed(called) { close(called) } }) var wasCalled bool // wait at most 100 milliseconds for the second call of ResolveUDPAddr that is supposed to fail ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) select { case <-called: wasCalled = true case <-ctx.Done(): fmt.Println("timed out") } cancel() return wasCalled } func isChannelClosed(ch <-chan struct{}) bool { select { case <-ch: return true default: } return false } func waitForConnCondition(conn *reconnectingUDPConn, condition func(conn *reconnectingUDPConn) bool) bool { var conditionVal bool for i := 0; i < 10; i++ { conn.connMtx.RLock() conditionVal = condition(conn) conn.connMtx.RUnlock() if conditionVal || i >= 9 { break } time.Sleep(time.Millisecond * 10) } return conditionVal } func newMockUDPAddr(t *testing.T, port int) *net.UDPAddr { var buf = make([]byte, 4) // random is not seeded to ensure tests are deterministic (also doesnt matter if ip is valid) _, err := rand.Read(buf) require.NoError(t, err) return &net.UDPAddr{ IP: net.IPv4(buf[0], buf[1], buf[2], buf[3]), Port: port, } } func TestNewResolvedUDPConn(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil). Once() dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil). Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Hour, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnWrites(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil). Once() dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil). Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Hour, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) assertConnWritable(t, conn, mockServer) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnEventuallyDials(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(nil, fmt.Errorf("failed to resolve")).Once(). On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil) dialer := mockDialer{} dialCall := dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil).Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Millisecond*10, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) err = conn.SetWriteBuffer(udpPacketMaxLength) assert.NoError(t, err) wasCalled := waitForCallWithTimeout(dialCall) assert.True(t, wasCalled) connEstablished := waitForConnCondition(conn, func(conn *reconnectingUDPConn) bool { return conn.conn != nil }) assert.True(t, connEstablished) assertConnWritable(t, conn, mockServer) assertSockBufferSize(t, udpPacketMaxLength, clientConn) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnNoSwapIfFail(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil).Once() failCall := resolver.On("ResolveUDPAddr", "udp", hostPort). Return(nil, fmt.Errorf("resolve failed")) dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil).Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Millisecond*10, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) wasCalled := waitForCallWithTimeout(failCall) assert.True(t, wasCalled) assertConnWritable(t, conn, mockServer) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnWriteRetry(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(nil, fmt.Errorf("failed to resolve")).Once(). On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil).Once() dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil).Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Millisecond*10, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) err = conn.SetWriteBuffer(udpPacketMaxLength) assert.NoError(t, err) assertConnWritable(t, conn, mockServer) assertSockBufferSize(t, udpPacketMaxLength, clientConn) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnWriteRetryFails(t *testing.T) { hostPort := "blahblah:34322" resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(nil, fmt.Errorf("failed to resolve")).Twice() dialer := mockDialer{} conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Millisecond*10, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) err = conn.SetWriteBuffer(udpPacketMaxLength) assert.NoError(t, err) _, err = conn.Write([]byte("yo this is a test")) assert.Error(t, err) err = conn.Close() assert.NoError(t, err) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnChanges(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr1 := newMockUDPAddr(t, 34322) mockServer2, clientConn2, err := newUDPConn() require.NoError(t, err) defer mockServer2.Close() mockUDPAddr2 := newMockUDPAddr(t, 34322) // ensure address doesn't duplicate mockUDPAddr1 for i := 0; i < 10 && mockUDPAddr2.IP.Equal(mockUDPAddr1.IP); i++ { mockUDPAddr2 = newMockUDPAddr(t, 34322) } // this is really unlikely to ever fail the test, but its here as a safeguard require.False(t, mockUDPAddr2.IP.Equal(mockUDPAddr1.IP)) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr1, nil).Once(). On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr2, nil) dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr1). Return(clientConn, nil).Once() secondDial := dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr2). Return(clientConn2, nil).Once() conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, time.Millisecond*10, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) err = conn.SetWriteBuffer(udpPacketMaxLength) assert.NoError(t, err) wasCalled := waitForCallWithTimeout(secondDial) assert.True(t, wasCalled) connSwapped := waitForConnCondition(conn, func(conn *reconnectingUDPConn) bool { return conn.conn == clientConn2 }) assert.True(t, connSwapped) assertConnWritable(t, conn, mockServer2) assertSockBufferSize(t, udpPacketMaxLength, clientConn2) err = conn.Close() assert.NoError(t, err) // assert the prev connection was closed assert.Error(t, clientConn.Close()) // assert the actual connection was closed assert.Error(t, clientConn2.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } func TestResolvedUDPConnLoopWithoutChanges(t *testing.T) { hostPort := "blahblah:34322" mockServer, clientConn, err := newUDPConn() require.NoError(t, err) defer mockServer.Close() mockUDPAddr := newMockUDPAddr(t, 34322) resolver := mockResolver{} resolver. On("ResolveUDPAddr", "udp", hostPort). Return(mockUDPAddr, nil) dialer := mockDialer{} dialer. On("DialUDP", "udp", (*net.UDPAddr)(nil), mockUDPAddr). Return(clientConn, nil). Once() resolveTimeout := 500 * time.Millisecond conn, err := newReconnectingUDPConn(hostPort, udpPacketMaxLength, resolveTimeout, resolver.ResolveUDPAddr, dialer.DialUDP, nil) assert.NoError(t, err) require.NotNil(t, conn) assert.Equal(t, mockUDPAddr, conn.destAddr) // Waiting for one round of loop time.Sleep(3 * resolveTimeout) assert.Equal(t, mockUDPAddr, conn.destAddr) err = conn.Close() assert.NoError(t, err) // assert the actual connection was closed assert.Error(t, clientConn.Close()) resolver.AssertExpectations(t) dialer.AssertExpectations(t) } golang-opentelemetry-otel-1.1.0/exporters/jaeger/uploader.go000066400000000000000000000237421414226744000242610ustar00rootroot00000000000000// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger" import ( "bytes" "context" "fmt" "io" "io/ioutil" "log" "net/http" "time" "go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift" gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger" ) // batchUploader send a batch of spans to Jaeger type batchUploader interface { upload(context.Context, *gen.Batch) error shutdown(context.Context) error } type EndpointOption interface { newBatchUploader() (batchUploader, error) } type endpointOptionFunc func() (batchUploader, error) func (fn endpointOptionFunc) newBatchUploader() (batchUploader, error) { return fn() } // WithAgentEndpoint configures the Jaeger exporter to send spans to a Jaeger agent // over compact thrift protocol. This will use the following environment variables for // configuration if no explicit option is provided: // // - OTEL_EXPORTER_JAEGER_AGENT_HOST is used for the agent address host // - OTEL_EXPORTER_JAEGER_AGENT_PORT is used for the agent address port // // The passed options will take precedence over any environment variables and default values // will be used if neither are provided. func WithAgentEndpoint(options ...AgentEndpointOption) EndpointOption { return endpointOptionFunc(func() (batchUploader, error) { cfg := &agentEndpointConfig{ agentClientUDPParams{ AttemptReconnecting: true, Host: envOr(envAgentHost, "localhost"), Port: envOr(envAgentPort, "6831"), }, } for _, opt := range options { opt.apply(cfg) } client, err := newAgentClientUDP(cfg.agentClientUDPParams) if err != nil { return nil, err } return &agentUploader{client: client}, nil }) } type AgentEndpointOption interface { apply(*agentEndpointConfig) } type agentEndpointConfig struct { agentClientUDPParams } type agentEndpointOptionFunc func(*agentEndpointConfig) func (fn agentEndpointOptionFunc) apply(cfg *agentEndpointConfig) { fn(cfg) } // WithAgentHost sets a host to be used in the agent client endpoint. // This option overrides any value set for the // OTEL_EXPORTER_JAEGER_AGENT_HOST environment variable. // If this option is not passed and the env var is not set, "localhost" will be used by default. func WithAgentHost(host string) AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.Host = host }) } // WithAgentPort sets a port to be used in the agent client endpoint. // This option overrides any value set for the // OTEL_EXPORTER_JAEGER_AGENT_PORT environment variable. // If this option is not passed and the env var is not set, "6831" will be used by default. func WithAgentPort(port string) AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.Port = port }) } // WithLogger sets a logger to be used by agent client. func WithLogger(logger *log.Logger) AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.Logger = logger }) } // WithDisableAttemptReconnecting sets option to disable reconnecting udp client. func WithDisableAttemptReconnecting() AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.AttemptReconnecting = false }) } // WithAttemptReconnectingInterval sets the interval between attempts to re resolve agent endpoint. func WithAttemptReconnectingInterval(interval time.Duration) AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.AttemptReconnectInterval = interval }) } // WithMaxPacketSize sets the maximum UDP packet size for transport to the Jaeger agent. func WithMaxPacketSize(size int) AgentEndpointOption { return agentEndpointOptionFunc(func(o *agentEndpointConfig) { o.MaxPacketSize = size }) } // WithCollectorEndpoint defines the full URL to the Jaeger HTTP Thrift collector. This will // use the following environment variables for configuration if no explicit option is provided: // // - OTEL_EXPORTER_JAEGER_ENDPOINT is the HTTP endpoint for sending spans directly to a collector. // - OTEL_EXPORTER_JAEGER_USER is the username to be sent as authentication to the collector endpoint. // - OTEL_EXPORTER_JAEGER_PASSWORD is the password to be sent as authentication to the collector endpoint. // // The passed options will take precedence over any environment variables. // If neither values are provided for the endpoint, the default value of "http://localhost:14268/api/traces" will be used. // If neither values are provided for the username or the password, they will not be set since there is no default. func WithCollectorEndpoint(options ...CollectorEndpointOption) EndpointOption { return endpointOptionFunc(func() (batchUploader, error) { cfg := &collectorEndpointConfig{ endpoint: envOr(envEndpoint, "http://localhost:14268/api/traces"), username: envOr(envUser, ""), password: envOr(envPassword, ""), httpClient: http.DefaultClient, } for _, opt := range options { opt.apply(cfg) } return &collectorUploader{ endpoint: cfg.endpoint, username: cfg.username, password: cfg.password, httpClient: cfg.httpClient, }, nil }) } type CollectorEndpointOption interface { apply(*collectorEndpointConfig) } type collectorEndpointConfig struct { // endpoint for sending spans directly to a collector. endpoint string // username to be used for authentication with the collector endpoint. username string // password to be used for authentication with the collector endpoint. password string // httpClient to be used to make requests to the collector endpoint. httpClient *http.Client } type collectorEndpointOptionFunc func(*collectorEndpointConfig) func (fn collectorEndpointOptionFunc) apply(cfg *collectorEndpointConfig) { fn(cfg) } // WithEndpoint is the URL for the Jaeger collector that spans are sent to. // This option overrides any value set for the // OTEL_EXPORTER_JAEGER_ENDPOINT environment variable. // If this option is not passed and the environment variable is not set, // "http://localhost:14268/api/traces" will be used by default. func WithEndpoint(endpoint string) CollectorEndpointOption { return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) { o.endpoint = endpoint }) } // WithUsername sets the username to be used in the authorization header sent for all requests to the collector. // This option overrides any value set for the // OTEL_EXPORTER_JAEGER_USER environment variable. // If this option is not passed and the environment variable is not set, no username will be set. func WithUsername(username string) CollectorEndpointOption { return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) { o.username = username }) } // WithPassword sets the password to be used in the authorization header sent for all requests to the collector. // This option overrides any value set for the // OTEL_EXPORTER_JAEGER_PASSWORD environment variable. // If this option is not passed and the environment variable is not set, no password will be set. func WithPassword(password string) CollectorEndpointOption { return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) { o.password = password }) } // WithHTTPClient sets the http client to be used to make request to the collector endpoint. func WithHTTPClient(client *http.Client) CollectorEndpointOption { return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) { o.httpClient = client }) } // agentUploader implements batchUploader interface sending batches to // Jaeger through the UDP agent. type agentUploader struct { client *agentClientUDP } var _ batchUploader = (*agentUploader)(nil) func (a *agentUploader) shutdown(ctx context.Context) error { done := make(chan error, 1) go func() { done <- a.client.Close() }() select { case <-ctx.Done(): // Prioritize not blocking the calling thread and just leak the // spawned goroutine to close the client. return ctx.Err() case err := <-done: return err } } func (a *agentUploader) upload(ctx context.Context, batch *gen.Batch) error { return a.client.EmitBatch(ctx, batch) } // collectorUploader implements batchUploader interface sending batches to // Jaeger through the collector http endpoint. type collectorUploader struct { endpoint string username string password string httpClient *http.Client } var _ batchUploader = (*collectorUploader)(nil) func (c *collectorUploader) shutdown(ctx context.Context) error { // The Exporter will cancel any active exports and will prevent all // subsequent exports, so nothing to do here. return nil } func (c *collectorUploader) upload(ctx context.Context, batch *gen.Batch) error { body, err := serialize(batch) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, "POST", c.endpoint, body) if err != nil { return err } if c.username != "" && c.password != "" { req.SetBasicAuth(c.username, c.password) } req.Header.Set("Content-Type", "application/x-thrift") resp, err := c.httpClient.Do(req) if err != nil { return err } _, _ = io.Copy(ioutil.Discard, resp.Body) resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("failed to upload traces; HTTP status code: %d", resp.StatusCode) } return nil } func serialize(obj thrift.TStruct) (*bytes.Buffer, error) { buf := thrift.NewTMemoryBuffer() if err := obj.Write(context.Background(), thrift.NewTBinaryProtocolConf(buf, &thrift.TConfiguration{})); err != nil { return nil, err } return buf.Buffer, nil } golang-opentelemetry-otel-1.1.0/exporters/otlp/000077500000000000000000000000001414226744000216305ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/000077500000000000000000000000001414226744000240125ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/clients.go000066400000000000000000000034301414226744000260020ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" 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 } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/exporter.go000066400000000000000000000070311414226744000262120ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" import ( "context" "errors" "sync" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform" "go.opentelemetry.io/otel/metric/sdkapi" metricsdk "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/resource" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) var ( errAlreadyStarted = errors.New("already started") ) // Exporter exports metrics data in the OTLP wire format. type Exporter struct { client Client temporalitySelector aggregation.TemporalitySelector mu sync.RWMutex started bool startOnce sync.Once stopOnce sync.Once } // Export exports a batch of metrics. func (e *Exporter) Export(ctx context.Context, res *resource.Resource, ilr metricsdk.InstrumentationLibraryReader) error { rm, err := metrictransform.InstrumentationLibraryReader(ctx, e, res, ilr, 1) if err != nil { return err } if rm == nil { return nil } // TODO: There is never more than one resource emitted by this // call, as per the specification. We can change the // signature of UploadMetrics correspondingly. Here create a // singleton list to reduce the size of the current PR: return e.client.UploadMetrics(ctx, []*metricpb.ResourceMetrics{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 } // Shutdown flushes all exports and closes all connections to the receiving endpoint. func (e *Exporter) Shutdown(ctx context.Context) error { e.mu.RLock() started := e.started e.mu.RUnlock() if !started { return nil } var err error e.stopOnce.Do(func() { err = e.client.Stop(ctx) e.mu.Lock() e.started = false e.mu.Unlock() }) return err } func (e *Exporter) TemporalityFor(descriptor *sdkapi.Descriptor, kind aggregation.Kind) aggregation.Temporality { return e.temporalitySelector.TemporalityFor(descriptor, kind) } var _ metricsdk.Exporter = (*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{ // Note: the default TemporalitySelector is specified // as Cumulative: // https://github.com/open-telemetry/opentelemetry-specification/issues/731 temporalitySelector: aggregation.CumulativeTemporalitySelector(), } for _, opt := range opts { opt.apply(&cfg) } e := &Exporter{ client: client, temporalitySelector: cfg.temporalitySelector, } return e } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/exporter_test.go000066400000000000000000000531661414226744000272630ustar00rootroot00000000000000// 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_test 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/testing/protocmp" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" metricsdk "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" commonpb "go.opentelemetry.io/proto/otlp/common/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) var ( // Timestamps used in this test: intervalStart = time.Now() intervalEnd = intervalStart.Add(time.Hour) ) type stubClient struct { rm []*metricpb.ResourceMetrics } func (m *stubClient) Start(ctx context.Context) error { return nil } func (m *stubClient) Stop(ctx context.Context) error { return nil } func (m *stubClient) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error { m.rm = append(m.rm, protoMetrics...) return nil } var _ otlpmetric.Client = (*stubClient)(nil) func (m *stubClient) Reset() { m.rm = nil } func newExporter(t *testing.T, opts ...otlpmetric.Option) (*otlpmetric.Exporter, *stubClient) { client := &stubClient{} exp, _ := otlpmetric.New(context.Background(), client, opts...) return exp, client } func startTime() uint64 { return uint64(intervalStart.UnixNano()) } func pointTime() uint64 { return uint64(intervalEnd.UnixNano()) } type testRecord struct { name string iKind sdkapi.InstrumentKind nKind number.Kind labels []attribute.KeyValue meterName string meterOpts []metric.MeterOption } func record( name string, iKind sdkapi.InstrumentKind, nKind number.Kind, labels []attribute.KeyValue, meterName string, meterOpts ...metric.MeterOption) testRecord { return testRecord{ name: name, iKind: iKind, nKind: nKind, labels: labels, meterName: meterName, meterOpts: meterOpts, } } var ( baseKeyValues = []attribute.KeyValue{attribute.String("host", "test.com")} cpuKey = attribute.Key("CPU") testHistogramBoundaries = []float64{2.0, 4.0, 8.0} cpu1Labels = []*commonpb.KeyValue{ { Key: "CPU", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 1, }, }, }, { Key: "host", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "test.com", }, }, }, } cpu2Labels = []*commonpb.KeyValue{ { Key: "CPU", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 2, }, }, }, { Key: "host", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "test.com", }, }, }, } testerAResource = resource.NewSchemaless(attribute.String("instance", "tester-a")) testerAResourcePb = metrictransform.Resource(testerAResource) ) const ( // Most of this test uses an empty instrumentation library name. testLibName = "" ) func TestNoGroupingExport(t *testing.T) { runMetricExportTests( t, nil, resource.Empty(), []testRecord{ record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(2)), testLibName, ), }, []*metricpb.ResourceMetrics{ { Resource: nil, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu2Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) } func TestHistogramMetricGroupingExport(t *testing.T) { r := record( "histogram", sdkapi.HistogramInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ) expected := []*metricpb.ResourceMetrics{ { Resource: nil, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "histogram", Data: &metricpb.Metric_Histogram{ Histogram: &metricpb.Histogram{ AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.HistogramDataPoint{ { Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), Count: 2, Sum: 11, ExplicitBounds: testHistogramBoundaries, BucketCounts: []uint64{1, 0, 0, 1}, }, { Attributes: cpu1Labels, Count: 2, Sum: 11, ExplicitBounds: testHistogramBoundaries, BucketCounts: []uint64{1, 0, 0, 1}, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, } runMetricExportTests(t, nil, resource.Empty(), []testRecord{r, r}, expected) } func TestCountInt64MetricGroupingExport(t *testing.T) { r := record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ) runMetricExportTests( t, nil, resource.Empty(), []testRecord{r, r}, []*metricpb.ResourceMetrics{ { Resource: nil, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) } func TestCountFloat64MetricGroupingExport(t *testing.T) { r := record( "float64-count", sdkapi.CounterInstrumentKind, number.Float64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ) runMetricExportTests( t, nil, resource.Empty(), []testRecord{r, r}, []*metricpb.ResourceMetrics{ { Resource: nil, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "float64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsDouble{AsDouble: 11.0}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsDouble{AsDouble: 11.0}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) } func TestResourceMetricGroupingExport(t *testing.T) { runMetricExportTests( t, nil, testerAResource, []testRecord{ record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(2)), testLibName, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ), }, []*metricpb.ResourceMetrics{ { Resource: testerAResourcePb, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu2Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) } func TestResourceInstLibMetricGroupingExport(t *testing.T) { version1 := metric.WithInstrumentationVersion("v1") version2 := metric.WithInstrumentationVersion("v2") specialSchema := metric.WithSchemaURL("schurl") summingLib := "summing-lib" countingLib := "counting-lib" runMetricExportTests( t, nil, testerAResource, []testRecord{ record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), countingLib, version1, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), countingLib, version2, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), countingLib, version1, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(2)), countingLib, version1, ), record( "int64-count", sdkapi.CounterInstrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), summingLib, specialSchema, ), }, []*metricpb.ResourceMetrics{ { Resource: testerAResourcePb, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { InstrumentationLibrary: &commonpb.InstrumentationLibrary{ Name: "counting-lib", Version: "v1", }, Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu2Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, { InstrumentationLibrary: &commonpb.InstrumentationLibrary{ Name: "counting-lib", Version: "v2", }, Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, { InstrumentationLibrary: &commonpb.InstrumentationLibrary{ Name: "summing-lib", }, SchemaUrl: "schurl", Metrics: []*metricpb.Metric{ { Name: "int64-count", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: true, AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) } func TestStatelessAggregationTemporality(t *testing.T) { type testcase struct { name string instrumentKind sdkapi.InstrumentKind aggTemporality metricpb.AggregationTemporality monotonic bool } for _, k := range []testcase{ {"counter", sdkapi.CounterInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, true}, {"updowncounter", sdkapi.UpDownCounterInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, false}, {"counterobserver", sdkapi.CounterObserverInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, true}, {"updowncounterobserver", sdkapi.UpDownCounterObserverInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, false}, } { t.Run(k.name, func(t *testing.T) { runMetricExportTests( t, []otlpmetric.Option{ otlpmetric.WithMetricAggregationTemporalitySelector( aggregation.StatelessTemporalitySelector(), ), }, testerAResource, []testRecord{ record( "instrument", k.instrumentKind, number.Int64Kind, append(baseKeyValues, cpuKey.Int(1)), testLibName, ), }, []*metricpb.ResourceMetrics{ { Resource: testerAResourcePb, InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ { Metrics: []*metricpb.Metric{ { Name: "instrument", Data: &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: k.monotonic, AggregationTemporality: k.aggTemporality, DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, Attributes: cpu1Labels, StartTimeUnixNano: startTime(), TimeUnixNano: pointTime(), }, }, }, }, }, }, }, }, }, }, ) }) } } func runMetricExportTests(t *testing.T, opts []otlpmetric.Option, res *resource.Resource, records []testRecord, expected []*metricpb.ResourceMetrics) { exp, driver := newExporter(t, opts...) libraryRecs := map[instrumentation.Library][]metricsdk.Record{} for _, r := range records { lcopy := make([]attribute.KeyValue, len(r.labels)) copy(lcopy, r.labels) desc := metrictest.NewDescriptor(r.name, r.iKind, r.nKind) labs := attribute.NewSet(lcopy...) var agg, ckpt metricsdk.Aggregator if r.iKind.Adding() { sums := sum.New(2) agg, ckpt = &sums[0], &sums[1] } else { histos := histogram.New(2, &desc, histogram.WithExplicitBoundaries(testHistogramBoundaries)) agg, ckpt = &histos[0], &histos[1] } ctx := context.Background() if r.iKind.Synchronous() { // For synchronous instruments, perform two updates: 1 and 10 switch r.nKind { case number.Int64Kind: require.NoError(t, agg.Update(ctx, number.NewInt64Number(1), &desc)) require.NoError(t, agg.Update(ctx, number.NewInt64Number(10), &desc)) case number.Float64Kind: require.NoError(t, agg.Update(ctx, number.NewFloat64Number(1), &desc)) require.NoError(t, agg.Update(ctx, number.NewFloat64Number(10), &desc)) default: t.Fatalf("invalid number kind: %v", r.nKind) } } else { // For asynchronous instruments, perform a single update: 11 switch r.nKind { case number.Int64Kind: require.NoError(t, agg.Update(ctx, number.NewInt64Number(11), &desc)) case number.Float64Kind: require.NoError(t, agg.Update(ctx, number.NewFloat64Number(11), &desc)) default: t.Fatalf("invalid number kind: %v", r.nKind) } } require.NoError(t, agg.SynchronizedMove(ckpt, &desc)) meterCfg := metric.NewMeterConfig(r.meterOpts...) lib := instrumentation.Library{ Name: r.meterName, Version: meterCfg.InstrumentationVersion(), SchemaURL: meterCfg.SchemaURL(), } libraryRecs[lib] = append(libraryRecs[lib], metricsdk.NewRecord(&desc, &labs, ckpt.Aggregation(), intervalStart, intervalEnd)) } assert.NoError(t, exp.Export(context.Background(), res, processortest.MultiInstrumentationLibraryReader(libraryRecs))) // assert.ElementsMatch does not equate nested slices of different order, // therefore this requires the top level slice to be broken down. // Build a map of Resource/InstrumentationLibrary pairs to Metrics, from // that validate the metric elements match for all expected pairs. Finally, // make we saw all expected pairs. keyFor := func(ilm *metricpb.InstrumentationLibraryMetrics) string { return fmt.Sprintf("%s/%s/%s", ilm.GetInstrumentationLibrary().GetName(), ilm.GetInstrumentationLibrary().GetVersion(), ilm.GetSchemaUrl()) } got := map[string][]*metricpb.Metric{} for _, rm := range driver.rm { for _, ilm := range rm.InstrumentationLibraryMetrics { k := keyFor(ilm) got[k] = append(got[k], ilm.GetMetrics()...) } } seen := map[string]struct{}{} for _, rm := range expected { for _, ilm := range rm.InstrumentationLibraryMetrics { k := keyFor(ilm) seen[k] = struct{}{} g, ok := got[k] if !ok { t.Errorf("missing metrics for:\n\tInstrumentationLibrary: %q\n", k) continue } if !assert.Len(t, g, len(ilm.GetMetrics())) { continue } for i, expected := range ilm.GetMetrics() { assert.Equal(t, "", cmp.Diff(expected, g[i], protocmp.Transform())) } } } for k := range got { if _, ok := seen[k]; !ok { t.Errorf("did not expect metrics for:\n\tInstrumentationLibrary: %s\n", k) } } } func TestEmptyMetricExport(t *testing.T) { exp, driver := newExporter(t) for _, test := range []struct { records []metricsdk.Record want []*metricpb.ResourceMetrics }{ { []metricsdk.Record(nil), []*metricpb.ResourceMetrics(nil), }, { []metricsdk.Record{}, []*metricpb.ResourceMetrics(nil), }, } { driver.Reset() require.NoError(t, exp.Export(context.Background(), resource.Empty(), processortest.MultiInstrumentationLibraryReader(map[instrumentation.Library][]metricsdk.Record{ { Name: testLibName, }: test.records, }))) assert.Equal(t, test.want, driver.rm) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/go.mod000066400000000000000000000061021414226744000251170ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlpmetric go 1.15 require ( github.com/cenkalti/backoff/v4 v4.1.1 github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.41.0 google.golang.org/protobuf v1.27.1 ) replace go.opentelemetry.io/otel => ../../.. replace go.opentelemetry.io/otel/sdk => ../../../sdk replace go.opentelemetry.io/otel/exporters/otlp => ../ replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ./ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ./otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/exporters/jaeger => ../../jaeger replace go.opentelemetry.io/otel/exporters/prometheus => ../../prometheus replace go.opentelemetry.io/otel/exporters/zipkin => ../../zipkin replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/go.sum000066400000000000000000000317041414226744000251520ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/000077500000000000000000000000001414226744000256265ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/connection/000077500000000000000000000000001414226744000277655ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/connection/alignment_test.go000066400000000000000000000020031414226744000333240ustar00rootroot00000000000000// 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 connection import ( "os" "testing" "unsafe" ottest "go.opentelemetry.io/otel/internal/internaltest" ) // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []ottest.FieldOffset{ { Name: "Connection.lastConnectErrPtr", Offset: unsafe.Offsetof(Connection{}.lastConnectErrPtr), }, } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/connection/connection.go000066400000000000000000000256361414226744000324670ustar00rootroot00000000000000// 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 connection import ( "context" "fmt" "math/rand" "sync" "sync/atomic" "time" "unsafe" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" "github.com/cenkalti/backoff/v4" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) type Connection struct { // Ensure pointer is 64-bit aligned for atomic operations on both 32 and 64 bit machines. lastConnectErrPtr unsafe.Pointer // mu protects the Connection as it is accessed by the // exporter goroutines and background Connection goroutine mu sync.Mutex cc *grpc.ClientConn // these fields are read-only after constructor is finished cfg otlpconfig.Config SCfg otlpconfig.SignalConfig metadata metadata.MD newConnectionHandler func(cc *grpc.ClientConn) // these channels are created once disconnectedCh chan bool backgroundConnectionDoneCh chan struct{} stopCh chan struct{} // this is for tests, so they can replace the closing // routine without a worry of modifying some global variable // or changing it back to original after the test is done closeBackgroundConnectionDoneCh func(ch chan struct{}) } func NewConnection(cfg otlpconfig.Config, sCfg otlpconfig.SignalConfig, handler func(cc *grpc.ClientConn)) *Connection { c := new(Connection) c.newConnectionHandler = handler c.cfg = cfg c.SCfg = sCfg if len(c.SCfg.Headers) > 0 { c.metadata = metadata.New(c.SCfg.Headers) } c.closeBackgroundConnectionDoneCh = func(ch chan struct{}) { close(ch) } return c } func (c *Connection) StartConnection(ctx context.Context) error { c.stopCh = make(chan struct{}) c.disconnectedCh = make(chan bool, 1) c.backgroundConnectionDoneCh = make(chan struct{}) if err := c.connect(ctx); err == nil { c.setStateConnected() } else { c.SetStateDisconnected(err) } go c.indefiniteBackgroundConnection() // TODO: proper error handling when initializing connections. // We can report permanent errors, e.g., invalid settings. return nil } func (c *Connection) LastConnectError() error { errPtr := (*error)(atomic.LoadPointer(&c.lastConnectErrPtr)) if errPtr == nil { return nil } return *errPtr } func (c *Connection) saveLastConnectError(err error) { var errPtr *error if err != nil { errPtr = &err } atomic.StorePointer(&c.lastConnectErrPtr, unsafe.Pointer(errPtr)) } func (c *Connection) SetStateDisconnected(err error) { c.saveLastConnectError(err) select { case c.disconnectedCh <- true: default: } c.newConnectionHandler(nil) } func (c *Connection) setStateConnected() { c.saveLastConnectError(nil) } func (c *Connection) Connected() bool { return c.LastConnectError() == nil } const defaultConnReattemptPeriod = 10 * time.Second func (c *Connection) indefiniteBackgroundConnection() { defer func() { c.closeBackgroundConnectionDoneCh(c.backgroundConnectionDoneCh) }() connReattemptPeriod := c.cfg.ReconnectionPeriod if connReattemptPeriod <= 0 { connReattemptPeriod = defaultConnReattemptPeriod } // No strong seeding required, nano time can // already help with pseudo uniqueness. rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024))) // maxJitterNanos: 70% of the connectionReattemptPeriod maxJitterNanos := int64(0.7 * float64(connReattemptPeriod)) for { // Otherwise these will be the normal scenarios to enable // reconnection if we trip out. // 1. If we've stopped, return entirely // 2. Otherwise block until we are disconnected, and // then retry connecting select { case <-c.stopCh: return case <-c.disconnectedCh: // Quickly check if we haven't stopped at the // same time. select { case <-c.stopCh: return default: } // Normal scenario that we'll wait for } if err := c.connect(context.Background()); err == nil { c.setStateConnected() } else { // this code is unreachable in most cases // c.connect does not establish Connection c.SetStateDisconnected(err) } // Apply some jitter to avoid lockstep retrials of other // collector-exporters. Lockstep retrials could result in an // innocent DDOS, by clogging the machine's resources and network. jitter := time.Duration(rng.Int63n(maxJitterNanos)) select { case <-c.stopCh: return case <-time.After(connReattemptPeriod + jitter): } } } func (c *Connection) connect(ctx context.Context) error { cc, err := c.dialToCollector(ctx) if err != nil { return err } c.setConnection(cc) c.newConnectionHandler(cc) return nil } // setConnection sets cc as the client Connection and returns true if // the Connection state changed. func (c *Connection) setConnection(cc *grpc.ClientConn) bool { c.mu.Lock() defer c.mu.Unlock() // If previous clientConn is same as the current then just return. // This doesn't happen right now as this func is only called with new ClientConn. // It is more about future-proofing. if c.cc == cc { return false } // If the previous clientConn was non-nil, close it if c.cc != nil { _ = c.cc.Close() } c.cc = cc return true } func (c *Connection) dialToCollector(ctx context.Context) (*grpc.ClientConn, error) { if c.cfg.GRPCConn != nil { return c.cfg.GRPCConn, nil } dialOpts := []grpc.DialOption{} if c.cfg.ServiceConfig != "" { dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(c.cfg.ServiceConfig)) } if c.SCfg.GRPCCredentials != nil { dialOpts = append(dialOpts, grpc.WithTransportCredentials(c.SCfg.GRPCCredentials)) } else if c.SCfg.Insecure { dialOpts = append(dialOpts, grpc.WithInsecure()) } if c.SCfg.Compression == otlpconfig.GzipCompression { dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name))) } if len(c.cfg.DialOptions) != 0 { dialOpts = append(dialOpts, c.cfg.DialOptions...) } ctx, cancel := c.ContextWithStop(ctx) defer cancel() ctx = c.ContextWithMetadata(ctx) return grpc.DialContext(ctx, c.SCfg.Endpoint, dialOpts...) } func (c *Connection) ContextWithMetadata(ctx context.Context) context.Context { if c.metadata.Len() > 0 { return metadata.NewOutgoingContext(ctx, c.metadata) } return ctx } func (c *Connection) Shutdown(ctx context.Context) error { close(c.stopCh) // Ensure that the backgroundConnector returns select { case <-c.backgroundConnectionDoneCh: case <-ctx.Done(): return ctx.Err() } c.mu.Lock() cc := c.cc c.cc = nil c.mu.Unlock() if cc != nil { return cc.Close() } return nil } func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { // Unify the parent context Done signal with the Connection's // stop channel. ctx, cancel := context.WithCancel(ctx) go func(ctx context.Context, cancel context.CancelFunc) { select { case <-ctx.Done(): // Nothing to do, either cancelled or deadline // happened. case <-c.stopCh: cancel() } }(ctx, cancel) return ctx, cancel } func (c *Connection) DoRequest(ctx context.Context, fn func(context.Context) error) error { expBackoff := newExponentialBackoff(c.cfg.RetrySettings) for { err := fn(ctx) if err == nil { // request succeeded. return nil } if !c.cfg.RetrySettings.Enabled { return err } // We have an error, check gRPC status code. st := status.Convert(err) if st.Code() == codes.OK { // Not really an error, still success. return nil } // Now, this is this a real error. if !shouldRetry(st.Code()) { // It is not a retryable error, we should not retry. return err } // Need to retry. throttle := getThrottleDuration(st) backoffDelay := expBackoff.NextBackOff() if backoffDelay == backoff.Stop { // throw away the batch err = fmt.Errorf("max elapsed time expired: %w", err) return err } var delay time.Duration if backoffDelay > throttle { delay = backoffDelay } else { if expBackoff.GetElapsedTime()+throttle > expBackoff.MaxElapsedTime { err = fmt.Errorf("max elapsed time expired when respecting server throttle: %w", err) return err } // Respect server throttling. delay = throttle } // back-off, but get interrupted when shutting down or request is cancelled or timed out. err = func() error { dt := time.NewTimer(delay) defer dt.Stop() select { case <-ctx.Done(): return ctx.Err() case <-c.stopCh: return fmt.Errorf("interrupted due to shutdown: %w", err) case <-dt.C: } return nil }() if err != nil { return err } } } func shouldRetry(code codes.Code) bool { switch code { case codes.OK: // Success. This function should not be called for this code, the best we // can do is tell the caller not to retry. return false case codes.Canceled, codes.DeadlineExceeded, codes.ResourceExhausted, codes.Aborted, codes.OutOfRange, codes.Unavailable, codes.DataLoss: // These are retryable errors. return true case codes.Unknown, codes.InvalidArgument, codes.Unauthenticated, codes.PermissionDenied, codes.NotFound, codes.AlreadyExists, codes.FailedPrecondition, codes.Unimplemented, codes.Internal: // These are fatal errors, don't retry. return false default: // Don't retry on unknown codes. return false } } func getThrottleDuration(status *status.Status) time.Duration { // See if throttling information is available. for _, detail := range status.Details() { if t, ok := detail.(*errdetails.RetryInfo); ok { if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 { // We are throttled. Wait before retrying as requested by the server. return time.Duration(t.RetryDelay.Seconds)*time.Second + time.Duration(t.RetryDelay.Nanos)*time.Nanosecond } return 0 } } return 0 } func newExponentialBackoff(rs otlpconfig.RetrySettings) *backoff.ExponentialBackOff { // Do not use NewExponentialBackOff since it calls Reset and the code here must // call Reset after changing the InitialInterval (this saves an unnecessary call to Now). expBackoff := &backoff.ExponentialBackOff{ InitialInterval: rs.InitialInterval, RandomizationFactor: backoff.DefaultRandomizationFactor, Multiplier: backoff.DefaultMultiplier, MaxInterval: rs.MaxInterval, MaxElapsedTime: rs.MaxElapsedTime, Stop: backoff.Stop, Clock: backoff.SystemClock, } expBackoff.Reset() return expBackoff } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/connection/connection_test.go000066400000000000000000000050051414226744000335120ustar00rootroot00000000000000// 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 connection import ( "testing" "time" "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" ) func TestGetThrottleDuration(t *testing.T) { tts := []struct { stsFn func() (*status.Status, error) throttle time.Duration }{ { stsFn: func() (*status.Status, error) { return status.New( codes.OK, "status with no retry info", ), nil }, throttle: 0, }, { stsFn: func() (*status.Status, error) { st := status.New(codes.ResourceExhausted, "status with retry info") return st.WithDetails( &errdetails.RetryInfo{RetryDelay: durationpb.New(15 * time.Millisecond)}, ) }, throttle: 15 * time.Millisecond, }, { stsFn: func() (*status.Status, error) { st := status.New(codes.ResourceExhausted, "status with error info detail") return st.WithDetails( &errdetails.ErrorInfo{Reason: "no throttle detail"}, ) }, throttle: 0, }, { stsFn: func() (*status.Status, error) { st := status.New(codes.ResourceExhausted, "status with error info and retry info") return st.WithDetails( &errdetails.ErrorInfo{Reason: "no throttle detail"}, &errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)}, ) }, throttle: 13 * time.Minute, }, { stsFn: func() (*status.Status, error) { st := status.New(codes.ResourceExhausted, "status with two retry info should take the first") return st.WithDetails( &errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)}, &errdetails.RetryInfo{RetryDelay: durationpb.New(18 * time.Minute)}, ) }, throttle: 13 * time.Minute, }, } for _, tt := range tts { sts, _ := tt.stsFn() t.Run(sts.Message(), func(t *testing.T) { th := getThrottleDuration(sts) require.Equal(t, tt.throttle, th) }) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/000077500000000000000000000000001414226744000310455ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/attribute.go000066400000000000000000000101101414226744000333700ustar00rootroot00000000000000// 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 metrictransform import ( "go.opentelemetry.io/otel/attribute" commonpb "go.opentelemetry.io/proto/otlp/common/v1" "go.opentelemetry.io/otel/sdk/resource" ) // KeyValues transforms a slice of attribute KeyValues into OTLP key-values. func KeyValues(attrs []attribute.KeyValue) []*commonpb.KeyValue { if len(attrs) == 0 { return nil } out := make([]*commonpb.KeyValue, 0, len(attrs)) for _, kv := range attrs { out = append(out, KeyValue(kv)) } return out } // Iterator transforms an attribute iterator into OTLP key-values. func Iterator(iter attribute.Iterator) []*commonpb.KeyValue { l := iter.Len() if l == 0 { return nil } out := make([]*commonpb.KeyValue, 0, l) for iter.Next() { out = append(out, KeyValue(iter.Attribute())) } return out } // ResourceAttributes transforms a Resource OTLP key-values. func ResourceAttributes(resource *resource.Resource) []*commonpb.KeyValue { return Iterator(resource.Iter()) } // KeyValue transforms an attribute KeyValue into an OTLP key-value. func KeyValue(kv attribute.KeyValue) *commonpb.KeyValue { return &commonpb.KeyValue{Key: string(kv.Key), Value: Value(kv.Value)} } // Value transforms an attribute Value into an OTLP AnyValue. func Value(v attribute.Value) *commonpb.AnyValue { av := new(commonpb.AnyValue) switch v.Type() { case attribute.BOOL: av.Value = &commonpb.AnyValue_BoolValue{ BoolValue: v.AsBool(), } case attribute.BOOLSLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: boolSliceValues(v.AsBoolSlice()), }, } case attribute.INT64: av.Value = &commonpb.AnyValue_IntValue{ IntValue: v.AsInt64(), } case attribute.INT64SLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: int64SliceValues(v.AsInt64Slice()), }, } case attribute.FLOAT64: av.Value = &commonpb.AnyValue_DoubleValue{ DoubleValue: v.AsFloat64(), } case attribute.FLOAT64SLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: float64SliceValues(v.AsFloat64Slice()), }, } case attribute.STRING: av.Value = &commonpb.AnyValue_StringValue{ StringValue: v.AsString(), } case attribute.STRINGSLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: stringSliceValues(v.AsStringSlice()), }, } default: av.Value = &commonpb.AnyValue_StringValue{ StringValue: "INVALID", } } return av } func boolSliceValues(vals []bool) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: v, }, } } return converted } func int64SliceValues(vals []int64) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: v, }, } } return converted } func float64SliceValues(vals []float64) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: v, }, } } return converted } func stringSliceValues(vals []string) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: v, }, } } return converted } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/attribute_test.go000066400000000000000000000152751414226744000344500ustar00rootroot00000000000000// 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 metrictransform import ( "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" commonpb "go.opentelemetry.io/proto/otlp/common/v1" ) type attributeTest struct { attrs []attribute.KeyValue expected []*commonpb.KeyValue } func TestAttributes(t *testing.T) { for _, test := range []attributeTest{ {nil, nil}, { []attribute.KeyValue{ attribute.Int("int to int", 123), attribute.Int64("int64 to int64", 1234567), attribute.Float64("float64 to double", 1.61), attribute.String("string to string", "string"), attribute.Bool("bool to bool", true), }, []*commonpb.KeyValue{ { Key: "int to int", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 123, }, }, }, { Key: "int64 to int64", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 1234567, }, }, }, { Key: "float64 to double", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: 1.61, }, }, }, { Key: "string to string", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "string", }, }, }, { Key: "bool to bool", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: true, }, }, }, }, }, } { got := KeyValues(test.attrs) if !assert.Len(t, got, len(test.expected)) { continue } for i, actual := range got { if a, ok := actual.Value.Value.(*commonpb.AnyValue_DoubleValue); ok { e, ok := test.expected[i].Value.Value.(*commonpb.AnyValue_DoubleValue) if !ok { t.Errorf("expected AnyValue_DoubleValue, got %T", test.expected[i].Value.Value) continue } if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { continue } e.DoubleValue = a.DoubleValue } assert.Equal(t, test.expected[i], actual) } } } func TestArrayAttributes(t *testing.T) { // Array KeyValue supports only arrays of primitive types: // "bool", "int", "int64", // "float64", "string", for _, test := range []attributeTest{ {nil, nil}, { []attribute.KeyValue{ { Key: attribute.Key("invalid"), Value: attribute.Value{}, }, }, []*commonpb.KeyValue{ { Key: "invalid", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "INVALID", }, }, }, }, }, { []attribute.KeyValue{ attribute.BoolSlice("bool slice to bool array", []bool{true, false}), attribute.IntSlice("int slice to int64 array", []int{1, 2, 3}), attribute.Int64Slice("int64 slice to int64 array", []int64{1, 2, 3}), attribute.Float64Slice("float64 slice to double array", []float64{1.11, 2.22, 3.33}), attribute.StringSlice("string slice to string array", []string{"foo", "bar", "baz"}), }, []*commonpb.KeyValue{ newOTelBoolArray("bool slice to bool array", []bool{true, false}), newOTelIntArray("int slice to int64 array", []int64{1, 2, 3}), newOTelIntArray("int64 slice to int64 array", []int64{1, 2, 3}), newOTelDoubleArray("float64 slice to double array", []float64{1.11, 2.22, 3.33}), newOTelStringArray("string slice to string array", []string{"foo", "bar", "baz"}), }, }, } { actualArrayAttributes := KeyValues(test.attrs) expectedArrayAttributes := test.expected if !assert.Len(t, actualArrayAttributes, len(expectedArrayAttributes)) { continue } for i, actualArrayAttr := range actualArrayAttributes { expectedArrayAttr := expectedArrayAttributes[i] expectedKey, actualKey := expectedArrayAttr.Key, actualArrayAttr.Key if !assert.Equal(t, expectedKey, actualKey) { continue } expected := expectedArrayAttr.Value.GetArrayValue() actual := actualArrayAttr.Value.GetArrayValue() if expected == nil { assert.Nil(t, actual) continue } if assert.NotNil(t, actual, "expected not nil for %s", actualKey) { assertExpectedArrayValues(t, expected.Values, actual.Values) } } } } func assertExpectedArrayValues(t *testing.T, expectedValues, actualValues []*commonpb.AnyValue) { for i, actual := range actualValues { expected := expectedValues[i] if a, ok := actual.Value.(*commonpb.AnyValue_DoubleValue); ok { e, ok := expected.Value.(*commonpb.AnyValue_DoubleValue) if !ok { t.Errorf("expected AnyValue_DoubleValue, got %T", expected.Value) continue } if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { continue } e.DoubleValue = a.DoubleValue } assert.Equal(t, expected, actual) } } func newOTelBoolArray(key string, values []bool) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, b := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: b, }, }) } return newOTelArray(key, arrayValues) } func newOTelIntArray(key string, values []int64) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, i := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: i, }, }) } return newOTelArray(key, arrayValues) } func newOTelDoubleArray(key string, values []float64) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, d := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: d, }, }) } return newOTelArray(key, arrayValues) } func newOTelStringArray(key string, values []string) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, s := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: s, }, }) } return newOTelArray(key, arrayValues) } func newOTelArray(key string, arrayValues []*commonpb.AnyValue) *commonpb.KeyValue { return &commonpb.KeyValue{ Key: key, Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: arrayValues, }, }, }, } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/metric.go000066400000000000000000000377111414226744000326700ustar00rootroot00000000000000// 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 transform provides translations for opentelemetry-go concepts and // structures to otlp structures. package metrictransform import ( "context" "errors" "fmt" "strings" "sync" "time" commonpb "go.opentelemetry.io/proto/otlp/common/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" "go.opentelemetry.io/otel/metric/number" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) var ( // ErrUnimplementedAgg is returned when a transformation of an unimplemented // aggregator is attempted. ErrUnimplementedAgg = errors.New("unimplemented aggregator") // ErrIncompatibleAgg is returned when // aggregation.Kind implies an interface conversion that has // failed ErrIncompatibleAgg = errors.New("incompatible aggregation type") // ErrUnknownValueType is returned when a transformation of an unknown value // is attempted. ErrUnknownValueType = errors.New("invalid value type") // ErrContextCanceled is returned when a context cancellation halts a // transformation. ErrContextCanceled = errors.New("context canceled") // ErrTransforming is returned when an unexected error is encountered transforming. ErrTransforming = errors.New("transforming failed") ) // result is the product of transforming Records into OTLP Metrics. type result struct { Metric *metricpb.Metric Err error } // toNanos returns the number of nanoseconds since the UNIX epoch. func toNanos(t time.Time) uint64 { if t.IsZero() { return 0 } return uint64(t.UnixNano()) } // InstrumentationLibraryReader transforms all records contained in a checkpoint into // batched OTLP ResourceMetrics. func InstrumentationLibraryReader(ctx context.Context, temporalitySelector aggregation.TemporalitySelector, res *resource.Resource, ilmr export.InstrumentationLibraryReader, numWorkers uint) (*metricpb.ResourceMetrics, error) { var ilms []*metricpb.InstrumentationLibraryMetrics err := ilmr.ForEach(func(lib instrumentation.Library, mr export.Reader) error { records, errc := source(ctx, temporalitySelector, mr) // Start a fixed number of goroutines to transform records. transformed := make(chan result) var wg sync.WaitGroup wg.Add(int(numWorkers)) for i := uint(0); i < numWorkers; i++ { go func() { defer wg.Done() transformer(ctx, temporalitySelector, records, transformed) }() } go func() { wg.Wait() close(transformed) }() // Synchronously collect the transformed records and transmit. ms, err := sink(ctx, transformed) if err != nil { return nil } // source is complete, check for any errors. if err := <-errc; err != nil { return err } if len(ms) == 0 { return nil } ilms = append(ilms, &metricpb.InstrumentationLibraryMetrics{ Metrics: ms, SchemaUrl: lib.SchemaURL, InstrumentationLibrary: &commonpb.InstrumentationLibrary{ Name: lib.Name, Version: lib.Version, }, }) return nil }) if len(ilms) == 0 { return nil, err } rms := &metricpb.ResourceMetrics{ Resource: Resource(res), SchemaUrl: res.SchemaURL(), InstrumentationLibraryMetrics: ilms, } return rms, err } // source starts a goroutine that sends each one of the Records yielded by // the Reader on the returned chan. Any error encountered will be sent // on the returned error chan after seeding is complete. func source(ctx context.Context, temporalitySelector aggregation.TemporalitySelector, mr export.Reader) (<-chan export.Record, <-chan error) { errc := make(chan error, 1) out := make(chan export.Record) // Seed records into process. go func() { defer close(out) // No select is needed since errc is buffered. errc <- mr.ForEach(temporalitySelector, func(r export.Record) error { select { case <-ctx.Done(): return ErrContextCanceled case out <- r: } return nil }) }() return out, errc } // transformer transforms records read from the passed in chan into // OTLP Metrics which are sent on the out chan. func transformer(ctx context.Context, temporalitySelector aggregation.TemporalitySelector, in <-chan export.Record, out chan<- result) { for r := range in { m, err := Record(temporalitySelector, r) // Propagate errors, but do not send empty results. if err == nil && m == nil { continue } res := result{ Metric: m, Err: err, } select { case <-ctx.Done(): return case out <- res: } } } // sink collects transformed Records and batches them. // // Any errors encountered transforming input will be reported with an // ErrTransforming as well as the completed ResourceMetrics. It is up to the // caller to handle any incorrect data in these ResourceMetric. func sink(ctx context.Context, in <-chan result) ([]*metricpb.Metric, error) { var errStrings []string // Group by the MetricDescriptor. grouped := map[string]*metricpb.Metric{} for res := range in { if res.Err != nil { errStrings = append(errStrings, res.Err.Error()) continue } mID := res.Metric.GetName() m, ok := grouped[mID] if !ok { grouped[mID] = res.Metric continue } // Note: There is extra work happening in this code // that can be improved when the work described in // #2119 is completed. The SDK has a guarantee that // no more than one point per period per label set is // produced, so this fallthrough should never happen. // The final step of #2119 is to remove all the // grouping logic here. switch res.Metric.Data.(type) { case *metricpb.Metric_Gauge: m.GetGauge().DataPoints = append(m.GetGauge().DataPoints, res.Metric.GetGauge().DataPoints...) case *metricpb.Metric_Sum: m.GetSum().DataPoints = append(m.GetSum().DataPoints, res.Metric.GetSum().DataPoints...) case *metricpb.Metric_Histogram: m.GetHistogram().DataPoints = append(m.GetHistogram().DataPoints, res.Metric.GetHistogram().DataPoints...) case *metricpb.Metric_Summary: m.GetSummary().DataPoints = append(m.GetSummary().DataPoints, res.Metric.GetSummary().DataPoints...) default: err := fmt.Sprintf("unsupported metric type: %T", res.Metric.Data) errStrings = append(errStrings, err) } } if len(grouped) == 0 { return nil, nil } ms := make([]*metricpb.Metric, 0, len(grouped)) for _, m := range grouped { ms = append(ms, m) } // Report any transform errors. if len(errStrings) > 0 { return ms, fmt.Errorf("%w:\n -%s", ErrTransforming, strings.Join(errStrings, "\n -")) } return ms, nil } // Record transforms a Record into an OTLP Metric. An ErrIncompatibleAgg // error is returned if the Record Aggregator is not supported. func Record(temporalitySelector aggregation.TemporalitySelector, r export.Record) (*metricpb.Metric, error) { agg := r.Aggregation() switch agg.Kind() { case aggregation.MinMaxSumCountKind: mmsc, ok := agg.(aggregation.MinMaxSumCount) if !ok { return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) } return minMaxSumCount(r, mmsc) case aggregation.HistogramKind: h, ok := agg.(aggregation.Histogram) if !ok { return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) } return histogramPoint(r, temporalitySelector.TemporalityFor(r.Descriptor(), aggregation.HistogramKind), h) case aggregation.SumKind: s, ok := agg.(aggregation.Sum) if !ok { return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) } sum, err := s.Sum() if err != nil { return nil, err } return sumPoint(r, sum, r.StartTime(), r.EndTime(), temporalitySelector.TemporalityFor(r.Descriptor(), aggregation.SumKind), r.Descriptor().InstrumentKind().Monotonic()) case aggregation.LastValueKind: lv, ok := agg.(aggregation.LastValue) if !ok { return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) } value, tm, err := lv.LastValue() if err != nil { return nil, err } return gaugePoint(r, value, time.Time{}, tm) case aggregation.ExactKind: e, ok := agg.(aggregation.Points) if !ok { return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) } pts, err := e.Points() if err != nil { return nil, err } return gaugeArray(r, pts) default: return nil, fmt.Errorf("%w: %T", ErrUnimplementedAgg, agg) } } func gaugeArray(record export.Record, points []aggregation.Point) (*metricpb.Metric, error) { desc := record.Descriptor() labels := record.Labels() m := &metricpb.Metric{ Name: desc.Name(), Description: desc.Description(), Unit: string(desc.Unit()), } pbAttrs := Iterator(labels.Iter()) ndp := make([]*metricpb.NumberDataPoint, 0, len(points)) switch nk := desc.NumberKind(); nk { case number.Int64Kind: for _, p := range points { ndp = append(ndp, &metricpb.NumberDataPoint{ Attributes: pbAttrs, StartTimeUnixNano: toNanos(record.StartTime()), TimeUnixNano: toNanos(record.EndTime()), Value: &metricpb.NumberDataPoint_AsInt{ AsInt: p.Number.CoerceToInt64(nk), }, }) } case number.Float64Kind: for _, p := range points { ndp = append(ndp, &metricpb.NumberDataPoint{ Attributes: pbAttrs, StartTimeUnixNano: toNanos(record.StartTime()), TimeUnixNano: toNanos(record.EndTime()), Value: &metricpb.NumberDataPoint_AsDouble{ AsDouble: p.Number.CoerceToFloat64(nk), }, }) } default: return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, nk) } m.Data = &metricpb.Metric_Gauge{ Gauge: &metricpb.Gauge{ DataPoints: ndp, }, } return m, nil } func gaugePoint(record export.Record, num number.Number, start, end time.Time) (*metricpb.Metric, error) { desc := record.Descriptor() labels := record.Labels() m := &metricpb.Metric{ Name: desc.Name(), Description: desc.Description(), Unit: string(desc.Unit()), } switch n := desc.NumberKind(); n { case number.Int64Kind: m.Data = &metricpb.Metric_Gauge{ Gauge: &metricpb.Gauge{ DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{ AsInt: num.CoerceToInt64(n), }, Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(start), TimeUnixNano: toNanos(end), }, }, }, } case number.Float64Kind: m.Data = &metricpb.Metric_Gauge{ Gauge: &metricpb.Gauge{ DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsDouble{ AsDouble: num.CoerceToFloat64(n), }, Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(start), TimeUnixNano: toNanos(end), }, }, }, } default: return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, n) } return m, nil } func sdkTemporalityToTemporality(temporality aggregation.Temporality) metricpb.AggregationTemporality { switch temporality { case aggregation.DeltaTemporality: return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA case aggregation.CumulativeTemporality: return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE } return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED } func sumPoint(record export.Record, num number.Number, start, end time.Time, temporality aggregation.Temporality, monotonic bool) (*metricpb.Metric, error) { desc := record.Descriptor() labels := record.Labels() m := &metricpb.Metric{ Name: desc.Name(), Description: desc.Description(), Unit: string(desc.Unit()), } switch n := desc.NumberKind(); n { case number.Int64Kind: m.Data = &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: monotonic, AggregationTemporality: sdkTemporalityToTemporality(temporality), DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsInt{ AsInt: num.CoerceToInt64(n), }, Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(start), TimeUnixNano: toNanos(end), }, }, }, } case number.Float64Kind: m.Data = &metricpb.Metric_Sum{ Sum: &metricpb.Sum{ IsMonotonic: monotonic, AggregationTemporality: sdkTemporalityToTemporality(temporality), DataPoints: []*metricpb.NumberDataPoint{ { Value: &metricpb.NumberDataPoint_AsDouble{ AsDouble: num.CoerceToFloat64(n), }, Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(start), TimeUnixNano: toNanos(end), }, }, }, } default: return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, n) } return m, nil } // minMaxSumCountValue returns the values of the MinMaxSumCount Aggregator // as discrete values. func minMaxSumCountValues(a aggregation.MinMaxSumCount) (min, max, sum number.Number, count uint64, err error) { if min, err = a.Min(); err != nil { return } if max, err = a.Max(); err != nil { return } if sum, err = a.Sum(); err != nil { return } if count, err = a.Count(); err != nil { return } return } // minMaxSumCount transforms a MinMaxSumCount Aggregator into an OTLP Metric. func minMaxSumCount(record export.Record, a aggregation.MinMaxSumCount) (*metricpb.Metric, error) { desc := record.Descriptor() labels := record.Labels() min, max, sum, count, err := minMaxSumCountValues(a) if err != nil { return nil, err } m := &metricpb.Metric{ Name: desc.Name(), Description: desc.Description(), Unit: string(desc.Unit()), Data: &metricpb.Metric_Summary{ Summary: &metricpb.Summary{ DataPoints: []*metricpb.SummaryDataPoint{ { Sum: sum.CoerceToFloat64(desc.NumberKind()), Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(record.StartTime()), TimeUnixNano: toNanos(record.EndTime()), Count: uint64(count), QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{ { Quantile: 0.0, Value: min.CoerceToFloat64(desc.NumberKind()), }, { Quantile: 1.0, Value: max.CoerceToFloat64(desc.NumberKind()), }, }, }, }, }, }, } return m, nil } func histogramValues(a aggregation.Histogram) (boundaries []float64, counts []uint64, err error) { var buckets aggregation.Buckets if buckets, err = a.Histogram(); err != nil { return } boundaries, counts = buckets.Boundaries, buckets.Counts if len(counts) != len(boundaries)+1 { err = ErrTransforming return } return } // histogram transforms a Histogram Aggregator into an OTLP Metric. func histogramPoint(record export.Record, temporality aggregation.Temporality, a aggregation.Histogram) (*metricpb.Metric, error) { desc := record.Descriptor() labels := record.Labels() boundaries, counts, err := histogramValues(a) if err != nil { return nil, err } count, err := a.Count() if err != nil { return nil, err } sum, err := a.Sum() if err != nil { return nil, err } m := &metricpb.Metric{ Name: desc.Name(), Description: desc.Description(), Unit: string(desc.Unit()), Data: &metricpb.Metric_Histogram{ Histogram: &metricpb.Histogram{ AggregationTemporality: sdkTemporalityToTemporality(temporality), DataPoints: []*metricpb.HistogramDataPoint{ { Sum: sum.CoerceToFloat64(desc.NumberKind()), Attributes: Iterator(labels.Iter()), StartTimeUnixNano: toNanos(record.StartTime()), TimeUnixNano: toNanos(record.EndTime()), Count: uint64(count), BucketCounts: counts, ExplicitBounds: boundaries, }, }, }, }, } return m, nil } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go000066400000000000000000000412021414226744000337150ustar00rootroot00000000000000// 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 metrictransform import ( "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" arrAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" lvAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" sumAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" commonpb "go.opentelemetry.io/proto/otlp/common/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) var ( // Timestamps used in this test: intervalStart = time.Now() intervalEnd = intervalStart.Add(time.Hour) ) const ( otelCumulative = metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE otelDelta = metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA ) func TestStringKeyValues(t *testing.T) { tests := []struct { kvs []attribute.KeyValue expected []*commonpb.KeyValue }{ { nil, nil, }, { []attribute.KeyValue{}, nil, }, { []attribute.KeyValue{ attribute.Bool("true", true), attribute.Int64("one", 1), attribute.Int64("two", 2), attribute.Float64("three", 3), attribute.Int("four", 4), attribute.Int("five", 5), attribute.Float64("six", 6), attribute.Int("seven", 7), attribute.Int("eight", 8), attribute.String("the", "final word"), }, []*commonpb.KeyValue{ {Key: "eight", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 8}}}, {Key: "five", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 5}}}, {Key: "four", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 4}}}, {Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 1}}}, {Key: "seven", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 7}}}, {Key: "six", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_DoubleValue{DoubleValue: 6.0}}}, {Key: "the", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "final word"}}}, {Key: "three", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_DoubleValue{DoubleValue: 3.0}}}, {Key: "true", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_BoolValue{BoolValue: true}}}, {Key: "two", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 2}}}, }, }, } for _, test := range tests { labels := attribute.NewSet(test.kvs...) assert.Equal(t, test.expected, Iterator(labels.Iter())) } } func TestMinMaxSumCountValue(t *testing.T) { mmscs := minmaxsumcount.New(2, &sdkapi.Descriptor{}) mmsc, ckpt := &mmscs[0], &mmscs[1] assert.NoError(t, mmsc.Update(context.Background(), 1, &sdkapi.Descriptor{})) assert.NoError(t, mmsc.Update(context.Background(), 10, &sdkapi.Descriptor{})) // Prior to checkpointing ErrNoData should be returned. _, _, _, _, err := minMaxSumCountValues(ckpt) assert.EqualError(t, err, aggregation.ErrNoData.Error()) // Checkpoint to set non-zero values require.NoError(t, mmsc.SynchronizedMove(ckpt, &sdkapi.Descriptor{})) min, max, sum, count, err := minMaxSumCountValues(ckpt) if assert.NoError(t, err) { assert.Equal(t, min, number.NewInt64Number(1)) assert.Equal(t, max, number.NewInt64Number(10)) assert.Equal(t, sum, number.NewInt64Number(11)) assert.Equal(t, count, uint64(2)) } } func TestMinMaxSumCountDatapoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind) labels := attribute.NewSet(attribute.String("one", "1")) mmscs := minmaxsumcount.New(2, &sdkapi.Descriptor{}) mmsc, ckpt := &mmscs[0], &mmscs[1] assert.NoError(t, mmsc.Update(context.Background(), 1, &desc)) assert.NoError(t, mmsc.Update(context.Background(), 10, &desc)) require.NoError(t, mmsc.SynchronizedMove(ckpt, &desc)) expected := []*metricpb.SummaryDataPoint{ { Count: 2, Sum: 11, StartTimeUnixNano: uint64(intervalStart.UnixNano()), TimeUnixNano: uint64(intervalEnd.UnixNano()), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{ { Quantile: 0.0, Value: 1.0, }, { Quantile: 1.0, Value: 10.0, }, }, }, } record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) m, err := minMaxSumCount(record, ckpt) if assert.NoError(t, err) { assert.Nil(t, m.GetGauge()) assert.Nil(t, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Equal(t, expected, m.GetSummary().DataPoints) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestMinMaxSumCountPropagatesErrors(t *testing.T) { // ErrNoData should be returned by both the Min and Max values of // a MinMaxSumCount Aggregator. Use this fact to check the error is // correctly returned. mmsc := &minmaxsumcount.New(1, &sdkapi.Descriptor{})[0] _, _, _, _, err := minMaxSumCountValues(mmsc) assert.Error(t, err) assert.Equal(t, aggregation.ErrNoData, err) } func TestSumIntDataPoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind) labels := attribute.NewSet(attribute.String("one", "1")) sums := sumAgg.New(2) s, ckpt := &sums[0], &sums[1] assert.NoError(t, s.Update(context.Background(), number.Number(1), &desc)) require.NoError(t, s.SynchronizedMove(ckpt, &desc)) record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) value, err := ckpt.Sum() require.NoError(t, err) if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), aggregation.CumulativeTemporality, true); assert.NoError(t, err) { assert.Nil(t, m.GetGauge()) assert.Equal(t, &metricpb.Sum{ AggregationTemporality: otelCumulative, IsMonotonic: true, DataPoints: []*metricpb.NumberDataPoint{{ StartTimeUnixNano: uint64(intervalStart.UnixNano()), TimeUnixNano: uint64(intervalEnd.UnixNano()), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, Value: &metricpb.NumberDataPoint_AsInt{ AsInt: 1, }, }}, }, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Nil(t, m.GetSummary()) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestSumFloatDataPoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Float64Kind) labels := attribute.NewSet(attribute.String("one", "1")) sums := sumAgg.New(2) s, ckpt := &sums[0], &sums[1] assert.NoError(t, s.Update(context.Background(), number.NewFloat64Number(1), &desc)) require.NoError(t, s.SynchronizedMove(ckpt, &desc)) record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) value, err := ckpt.Sum() require.NoError(t, err) if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), aggregation.DeltaTemporality, false); assert.NoError(t, err) { assert.Nil(t, m.GetGauge()) assert.Equal(t, &metricpb.Sum{ IsMonotonic: false, AggregationTemporality: otelDelta, DataPoints: []*metricpb.NumberDataPoint{{ Value: &metricpb.NumberDataPoint_AsDouble{ AsDouble: 1.0, }, StartTimeUnixNano: uint64(intervalStart.UnixNano()), TimeUnixNano: uint64(intervalEnd.UnixNano()), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, }}}, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Nil(t, m.GetSummary()) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestLastValueIntDataPoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind) labels := attribute.NewSet(attribute.String("one", "1")) lvs := lvAgg.New(2) lv, ckpt := &lvs[0], &lvs[1] assert.NoError(t, lv.Update(context.Background(), number.Number(100), &desc)) require.NoError(t, lv.SynchronizedMove(ckpt, &desc)) record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) value, timestamp, err := ckpt.LastValue() require.NoError(t, err) if m, err := gaugePoint(record, value, time.Time{}, timestamp); assert.NoError(t, err) { assert.Equal(t, []*metricpb.NumberDataPoint{{ StartTimeUnixNano: 0, TimeUnixNano: uint64(timestamp.UnixNano()), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, Value: &metricpb.NumberDataPoint_AsInt{ AsInt: 100, }, }}, m.GetGauge().DataPoints) assert.Nil(t, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Nil(t, m.GetSummary()) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestExactIntDataPoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Int64Kind) labels := attribute.NewSet(attribute.String("one", "1")) arrs := arrAgg.New(2) e, ckpt := &arrs[0], &arrs[1] assert.NoError(t, e.Update(context.Background(), number.Number(100), &desc)) require.NoError(t, e.SynchronizedMove(ckpt, &desc)) record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) pts, err := ckpt.Points() require.NoError(t, err) if m, err := gaugeArray(record, pts); assert.NoError(t, err) { assert.Equal(t, []*metricpb.NumberDataPoint{{ StartTimeUnixNano: toNanos(intervalStart), TimeUnixNano: toNanos(intervalEnd), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, Value: &metricpb.NumberDataPoint_AsInt{ AsInt: 100, }, }}, m.GetGauge().DataPoints) assert.Nil(t, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Nil(t, m.GetSummary()) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestExactFloatDataPoints(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Float64Kind) labels := attribute.NewSet(attribute.String("one", "1")) arrs := arrAgg.New(2) e, ckpt := &arrs[0], &arrs[1] assert.NoError(t, e.Update(context.Background(), number.NewFloat64Number(100), &desc)) require.NoError(t, e.SynchronizedMove(ckpt, &desc)) record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) pts, err := ckpt.Points() require.NoError(t, err) if m, err := gaugeArray(record, pts); assert.NoError(t, err) { assert.Equal(t, []*metricpb.NumberDataPoint{{ Value: &metricpb.NumberDataPoint_AsDouble{ AsDouble: 100, }, StartTimeUnixNano: toNanos(intervalStart), TimeUnixNano: toNanos(intervalEnd), Attributes: []*commonpb.KeyValue{ { Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, }, }, }}, m.GetGauge().DataPoints) assert.Nil(t, m.GetSum()) assert.Nil(t, m.GetHistogram()) assert.Nil(t, m.GetSummary()) assert.Nil(t, m.GetIntGauge()) // nolint assert.Nil(t, m.GetIntSum()) // nolint assert.Nil(t, m.GetIntHistogram()) // nolint } } func TestSumErrUnknownValueType(t *testing.T) { desc := metrictest.NewDescriptor("", sdkapi.HistogramInstrumentKind, number.Kind(-1)) labels := attribute.NewSet() s := &sumAgg.New(1)[0] record := export.NewRecord(&desc, &labels, s, intervalStart, intervalEnd) value, err := s.Sum() require.NoError(t, err) _, err = sumPoint(record, value, record.StartTime(), record.EndTime(), aggregation.CumulativeTemporality, true) assert.Error(t, err) if !errors.Is(err, ErrUnknownValueType) { t.Errorf("expected ErrUnknownValueType, got %v", err) } } type testAgg struct { kind aggregation.Kind agg aggregation.Aggregation } func (t *testAgg) Kind() aggregation.Kind { return t.kind } func (t *testAgg) Aggregation() aggregation.Aggregation { return t.agg } // None of these three are used: func (t *testAgg) Update(ctx context.Context, number number.Number, descriptor *sdkapi.Descriptor) error { return nil } func (t *testAgg) SynchronizedMove(destination export.Aggregator, descriptor *sdkapi.Descriptor) error { return nil } func (t *testAgg) Merge(aggregator export.Aggregator, descriptor *sdkapi.Descriptor) error { return nil } type testErrSum struct { err error } type testErrLastValue struct { err error } type testErrMinMaxSumCount struct { testErrSum } func (te *testErrLastValue) LastValue() (number.Number, time.Time, error) { return 0, time.Time{}, te.err } func (te *testErrLastValue) Kind() aggregation.Kind { return aggregation.LastValueKind } func (te *testErrSum) Sum() (number.Number, error) { return 0, te.err } func (te *testErrSum) Kind() aggregation.Kind { return aggregation.SumKind } func (te *testErrMinMaxSumCount) Min() (number.Number, error) { return 0, te.err } func (te *testErrMinMaxSumCount) Max() (number.Number, error) { return 0, te.err } func (te *testErrMinMaxSumCount) Count() (uint64, error) { return 0, te.err } var _ export.Aggregator = &testAgg{} var _ aggregation.Aggregation = &testAgg{} var _ aggregation.Sum = &testErrSum{} var _ aggregation.LastValue = &testErrLastValue{} var _ aggregation.MinMaxSumCount = &testErrMinMaxSumCount{} func TestRecordAggregatorIncompatibleErrors(t *testing.T) { makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { desc := metrictest.NewDescriptor("things", sdkapi.CounterInstrumentKind, number.Int64Kind) labels := attribute.NewSet() test := &testAgg{ kind: kind, agg: agg, } return Record(aggregation.CumulativeTemporalitySelector(), export.NewRecord(&desc, &labels, test, intervalStart, intervalEnd)) } mpb, err := makeMpb(aggregation.SumKind, &lastvalue.New(1)[0]) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, ErrIncompatibleAgg)) mpb, err = makeMpb(aggregation.LastValueKind, &sum.New(1)[0]) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, ErrIncompatibleAgg)) mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &lastvalue.New(1)[0]) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, ErrIncompatibleAgg)) mpb, err = makeMpb(aggregation.ExactKind, &lastvalue.New(1)[0]) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, ErrIncompatibleAgg)) } func TestRecordAggregatorUnexpectedErrors(t *testing.T) { makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { desc := metrictest.NewDescriptor("things", sdkapi.CounterInstrumentKind, number.Int64Kind) labels := attribute.NewSet() return Record(aggregation.CumulativeTemporalitySelector(), export.NewRecord(&desc, &labels, agg, intervalStart, intervalEnd)) } errEx := fmt.Errorf("timeout") mpb, err := makeMpb(aggregation.SumKind, &testErrSum{errEx}) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, errEx)) mpb, err = makeMpb(aggregation.LastValueKind, &testErrLastValue{errEx}) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, errEx)) mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &testErrMinMaxSumCount{testErrSum{errEx}}) require.Error(t, err) require.Nil(t, mpb) require.True(t, errors.Is(err, errEx)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/resource.go000066400000000000000000000016631414226744000332310ustar00rootroot00000000000000// 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 metrictransform import ( resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" "go.opentelemetry.io/otel/sdk/resource" ) // Resource transforms a Resource into an OTLP Resource. func Resource(r *resource.Resource) *resourcepb.Resource { if r == nil { return nil } return &resourcepb.Resource{Attributes: ResourceAttributes(r)} } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/metrictransform/resource_test.go000066400000000000000000000025551414226744000342710ustar00rootroot00000000000000// 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 metrictransform import ( "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" ) func TestNilResource(t *testing.T) { assert.Empty(t, Resource(nil)) } func TestEmptyResource(t *testing.T) { assert.Empty(t, Resource(&resource.Resource{})) } /* * This does not include any testing on the ordering of Resource Attributes. * They are stored as a map internally to the Resource and their order is not * guaranteed. */ func TestResourceAttributes(t *testing.T) { attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} got := Resource(resource.NewSchemaless(attrs...)).GetAttributes() if !assert.Len(t, attrs, 2) { return } assert.ElementsMatch(t, KeyValues(attrs), got) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/000077500000000000000000000000001414226744000277725ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/envconfig.go000066400000000000000000000116061414226744000323030ustar00rootroot00000000000000// 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 otlpconfig import ( "crypto/tls" "fmt" "io/ioutil" "net/url" "os" "regexp" "strconv" "strings" "time" "go.opentelemetry.io/otel" ) var httpSchemeRegexp = regexp.MustCompile(`(?i)^http://|https://`) func ApplyGRPCEnvConfigs(cfg *Config) { e := EnvOptionsReader{ GetEnv: os.Getenv, ReadFile: ioutil.ReadFile, } e.ApplyGRPCEnvConfigs(cfg) } func ApplyHTTPEnvConfigs(cfg *Config) { e := EnvOptionsReader{ GetEnv: os.Getenv, ReadFile: ioutil.ReadFile, } e.ApplyHTTPEnvConfigs(cfg) } type EnvOptionsReader struct { GetEnv func(string) string ReadFile func(filename string) ([]byte, error) } func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) { opts := e.GetOptionsFromEnv() for _, opt := range opts { opt.ApplyHTTPOption(cfg) } } func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg *Config) { opts := e.GetOptionsFromEnv() for _, opt := range opts { opt.ApplyGRPCOption(cfg) } } func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption { var opts []GenericOption // Endpoint if v, ok := e.getEnvValue("ENDPOINT"); ok { if isInsecureEndpoint(v) { opts = append(opts, WithInsecure()) } else { opts = append(opts, WithSecure()) } opts = append(opts, WithEndpoint(trimSchema(v))) } if v, ok := e.getEnvValue("METRICS_ENDPOINT"); ok { if isInsecureEndpoint(v) { opts = append(opts, WithInsecure()) } else { opts = append(opts, WithSecure()) } opts = append(opts, WithEndpoint(trimSchema(v))) } // Certificate File if path, ok := e.getEnvValue("CERTIFICATE"); ok { if tls, err := e.readTLSConfig(path); err == nil { opts = append(opts, WithTLSClientConfig(tls)) } else { otel.Handle(fmt.Errorf("failed to configure otlp exporter certificate '%s': %w", path, err)) } } if path, ok := e.getEnvValue("METRICS_CERTIFICATE"); ok { if tls, err := e.readTLSConfig(path); err == nil { opts = append(opts, WithTLSClientConfig(tls)) } else { otel.Handle(fmt.Errorf("failed to configure otlp exporter certificate '%s': %w", path, err)) } } // Headers if h, ok := e.getEnvValue("HEADERS"); ok { opts = append(opts, WithHeaders(stringToHeader(h))) } if h, ok := e.getEnvValue("METRICS_HEADERS"); ok { opts = append(opts, WithHeaders(stringToHeader(h))) } // Compression if c, ok := e.getEnvValue("COMPRESSION"); ok { opts = append(opts, WithCompression(stringToCompression(c))) } if c, ok := e.getEnvValue("METRICS_COMPRESSION"); ok { opts = append(opts, WithCompression(stringToCompression(c))) } // Timeout if t, ok := e.getEnvValue("TIMEOUT"); ok { if d, err := strconv.Atoi(t); err == nil { opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond)) } } if t, ok := e.getEnvValue("METRICS_TIMEOUT"); ok { if d, err := strconv.Atoi(t); err == nil { opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond)) } } return opts } func isInsecureEndpoint(endpoint string) bool { return strings.HasPrefix(strings.ToLower(endpoint), "http://") || strings.HasPrefix(strings.ToLower(endpoint), "unix://") } func trimSchema(endpoint string) string { return httpSchemeRegexp.ReplaceAllString(endpoint, "") } // getEnvValue gets an OTLP environment variable value of the specified key using the GetEnv function. // This function already prepends the OTLP prefix to all key lookup. func (e *EnvOptionsReader) getEnvValue(key string) (string, bool) { v := strings.TrimSpace(e.GetEnv(fmt.Sprintf("OTEL_EXPORTER_OTLP_%s", key))) return v, v != "" } func (e *EnvOptionsReader) readTLSConfig(path string) (*tls.Config, error) { b, err := e.ReadFile(path) if err != nil { return nil, err } return CreateTLSConfig(b) } func stringToCompression(value string) Compression { switch value { case "gzip": return GzipCompression } return NoCompression } func stringToHeader(value string) map[string]string { headersPairs := strings.Split(value, ",") headers := make(map[string]string) for _, header := range headersPairs { nameValue := strings.SplitN(header, "=", 2) if len(nameValue) < 2 { continue } name, err := url.QueryUnescape(nameValue[0]) if err != nil { continue } trimmedName := strings.TrimSpace(name) value, err := url.QueryUnescape(nameValue[1]) if err != nil { continue } trimmedValue := strings.TrimSpace(value) headers[trimmedName] = trimmedValue } return headers } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/envconfig_test.go000066400000000000000000000034351414226744000333430ustar00rootroot00000000000000// 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 otlpconfig import ( "reflect" "testing" ) func TestStringToHeader(t *testing.T) { tests := []struct { name string value string want map[string]string }{ { name: "simple test", value: "userId=alice", want: map[string]string{"userId": "alice"}, }, { name: "simple test with spaces", value: " userId = alice ", want: map[string]string{"userId": "alice"}, }, { name: "multiples headers encoded", value: "userId=alice,serverNode=DF%3A28,isProduction=false", want: map[string]string{ "userId": "alice", "serverNode": "DF:28", "isProduction": "false", }, }, { name: "invalid headers format", value: "userId:alice", want: map[string]string{}, }, { name: "invalid key", value: "%XX=missing,userId=alice", want: map[string]string{ "userId": "alice", }, }, { name: "invalid value", value: "missing=%XX,userId=alice", want: map[string]string{ "userId": "alice", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := stringToHeader(tt.value); !reflect.DeepEqual(got, tt.want) { t.Errorf("stringToHeader() = %v, want %v", got, tt.want) } }) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/options.go000066400000000000000000000145571414226744000320300ustar00rootroot00000000000000// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" import ( "crypto/tls" "fmt" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) const ( // DefaultMaxAttempts describes how many times the driver // should retry the sending of the payload in case of a // retryable error. DefaultMaxAttempts int = 5 // DefaultMetricsPath is a default URL path for endpoint that // receives metrics. DefaultMetricsPath string = "/v1/metrics" // DefaultBackoff is a default base backoff time used in the // exponential backoff strategy. DefaultBackoff time.Duration = 300 * time.Millisecond // DefaultTimeout is a default max waiting time for the backend to process // each span or metrics batch. DefaultTimeout time.Duration = 10 * time.Second ) var ( // defaultRetrySettings is a default settings for the retry policy. defaultRetrySettings = RetrySettings{ Enabled: true, InitialInterval: 5 * time.Second, MaxInterval: 30 * time.Second, MaxElapsedTime: time.Minute, } ) type ( SignalConfig struct { Endpoint string Insecure bool TLSCfg *tls.Config Headers map[string]string Compression Compression Timeout time.Duration URLPath string // gRPC configurations GRPCCredentials credentials.TransportCredentials } Config struct { // Signal specific configurations Metrics SignalConfig // HTTP configurations MaxAttempts int Backoff time.Duration // gRPC configurations ReconnectionPeriod time.Duration ServiceConfig string DialOptions []grpc.DialOption GRPCConn *grpc.ClientConn RetrySettings RetrySettings } ) func NewDefaultConfig() Config { c := Config{ Metrics: SignalConfig{ Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorPort), URLPath: DefaultMetricsPath, Compression: NoCompression, Timeout: DefaultTimeout, }, RetrySettings: defaultRetrySettings, } return c } type ( // GenericOption applies an option to the HTTP or gRPC driver. GenericOption interface { ApplyHTTPOption(*Config) ApplyGRPCOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } // HTTPOption applies an option to the HTTP driver. HTTPOption interface { ApplyHTTPOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } // GRPCOption applies an option to the gRPC driver. GRPCOption interface { ApplyGRPCOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } ) // genericOption is an option that applies the same logic // for both gRPC and HTTP. type genericOption struct { fn func(*Config) } func (g *genericOption) ApplyGRPCOption(cfg *Config) { g.fn(cfg) } func (g *genericOption) ApplyHTTPOption(cfg *Config) { g.fn(cfg) } func (genericOption) private() {} func newGenericOption(fn func(cfg *Config)) GenericOption { return &genericOption{fn: fn} } // splitOption is an option that applies different logics // for gRPC and HTTP. type splitOption struct { httpFn func(*Config) grpcFn func(*Config) } func (g *splitOption) ApplyGRPCOption(cfg *Config) { g.grpcFn(cfg) } func (g *splitOption) ApplyHTTPOption(cfg *Config) { g.httpFn(cfg) } func (splitOption) private() {} func newSplitOption(httpFn func(cfg *Config), grpcFn func(cfg *Config)) GenericOption { return &splitOption{httpFn: httpFn, grpcFn: grpcFn} } // httpOption is an option that is only applied to the HTTP driver. type httpOption struct { fn func(*Config) } func (h *httpOption) ApplyHTTPOption(cfg *Config) { h.fn(cfg) } func (httpOption) private() {} func NewHTTPOption(fn func(cfg *Config)) HTTPOption { return &httpOption{fn: fn} } // grpcOption is an option that is only applied to the gRPC driver. type grpcOption struct { fn func(*Config) } func (h *grpcOption) ApplyGRPCOption(cfg *Config) { h.fn(cfg) } func (grpcOption) private() {} func NewGRPCOption(fn func(cfg *Config)) GRPCOption { return &grpcOption{fn: fn} } // Generic Options func WithEndpoint(endpoint string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Endpoint = endpoint }) } func WithCompression(compression Compression) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Compression = compression }) } func WithURLPath(urlPath string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.URLPath = urlPath }) } func WithRetry(settings RetrySettings) GenericOption { return newGenericOption(func(cfg *Config) { cfg.RetrySettings = settings }) } func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption { return newSplitOption(func(cfg *Config) { cfg.Metrics.TLSCfg = tlsCfg.Clone() }, func(cfg *Config) { cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg) }) } func WithInsecure() GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Insecure = true }) } func WithSecure() GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Insecure = false }) } func WithHeaders(headers map[string]string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Headers = headers }) } func WithTimeout(duration time.Duration) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Metrics.Timeout = duration }) } func WithMaxAttempts(maxAttempts int) GenericOption { return newGenericOption(func(cfg *Config) { cfg.MaxAttempts = maxAttempts }) } func WithBackoff(duration time.Duration) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Backoff = duration }) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/options_test.go000066400000000000000000000312111414226744000330510ustar00rootroot00000000000000// 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 otlpconfig_test import ( "errors" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" "github.com/stretchr/testify/assert" ) const ( WeakCertificate = ` -----BEGIN CERTIFICATE----- MIIBhzCCASygAwIBAgIRANHpHgAWeTnLZpTSxCKs0ggwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEChMHb3RlbC1nbzAeFw0yMTA0MDExMzU5MDNaFw0yMTA0MDExNDU5MDNa MBIxEDAOBgNVBAoTB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS9 nWSkmPCxShxnp43F+PrOtbGV7sNfkbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0Z sJCLHGogQsYnWJBXUZOVo2MwYTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAsBgNVHREEJTAjgglsb2NhbGhvc3SHEAAA AAAAAAAAAAAAAAAAAAGHBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhANwZVVKvfvQ/ 1HXsTvgH+xTQswOwSSKYJ1cVHQhqK7ZbAiEAus8NxpTRnp5DiTMuyVmhVNPB+bVH Lhnm4N/QDk5rek0= -----END CERTIFICATE----- ` WeakPrivateKey = ` -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgN8HEXiXhvByrJ1zK SFT6Y2l2KqDWwWzKf+t4CyWrNKehRANCAAS9nWSkmPCxShxnp43F+PrOtbGV7sNf kbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0ZsJCLHGogQsYnWJBXUZOV -----END PRIVATE KEY----- ` ) type env map[string]string func (e *env) getEnv(env string) string { return (*e)[env] } type fileReader map[string][]byte func (f *fileReader) readFile(filename string) ([]byte, error) { if b, ok := (*f)[filename]; ok { return b, nil } return nil, errors.New("File not found") } func TestConfigs(t *testing.T) { tlsCert, err := otlpconfig.CreateTLSConfig([]byte(WeakCertificate)) assert.NoError(t, err) tests := []struct { name string opts []otlpconfig.GenericOption env env fileReader fileReader asserts func(t *testing.T, c *otlpconfig.Config, grpcOption bool) }{ { name: "Test default configs", asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "localhost:4317", c.Metrics.Endpoint) assert.Equal(t, otlpconfig.NoCompression, c.Metrics.Compression) assert.Equal(t, map[string]string(nil), c.Metrics.Headers) assert.Equal(t, 10*time.Second, c.Metrics.Timeout) }, }, // Endpoint Tests { name: "Test With Endpoint", opts: []otlpconfig.GenericOption{ otlpconfig.WithEndpoint("someendpoint"), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "someendpoint", c.Metrics.Endpoint) }, }, { name: "Test Environment Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) }, }, { name: "Test Environment Signal Specific Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) }, }, { name: "Test Mixed Environment and With Endpoint", opts: []otlpconfig.GenericOption{ otlpconfig.WithEndpoint("metrics_endpoint"), }, env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "metrics_endpoint", c.Metrics.Endpoint) }, }, { name: "Test Environment Endpoint with HTTP scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, true, c.Metrics.Insecure) }, }, { name: "Test Environment Endpoint with HTTP scheme and leading & trailingspaces", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": " http://env_endpoint ", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, true, c.Metrics.Insecure) }, }, { name: "Test Environment Endpoint with HTTPS scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "https://env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) assert.Equal(t, false, c.Metrics.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "https://env_metrics_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) assert.Equal(t, false, c.Metrics.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint #2", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) assert.Equal(t, false, c.Metrics.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint with uppercase scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "HTTP://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) assert.Equal(t, false, c.Metrics.Insecure) }, }, // Certificate tests { name: "Test With Certificate", opts: []otlpconfig.GenericOption{ otlpconfig.WithTLSClientConfig(tlsCert), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { if grpcOption { //TODO: make sure gRPC's credentials actually works assert.NotNil(t, c.Metrics.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Metrics.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Environment Certificate", 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) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Metrics.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Environment Signal Specific Certificate", env: map[string]string{ "OTEL_EXPORTER_OTLP_CERTIFICATE": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE": "cert_path", }, fileReader: fileReader{ "cert_path": []byte(WeakCertificate), "invalid_cert": []byte("invalid certificate file."), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Metrics.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Mixed Environment and With Certificate", opts: []otlpconfig.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) { if grpcOption { assert.NotNil(t, c.Metrics.GRPCCredentials) } else { assert.Equal(t, 1, len(c.Metrics.TLSCfg.RootCAs.Subjects())) } }, }, // Headers tests { name: "Test With Headers", opts: []otlpconfig.GenericOption{ otlpconfig.WithHeaders(map[string]string{"h1": "v1"}), }, asserts: func(t *testing.T, c *otlpconfig.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) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Metrics.Headers) }, }, { name: "Test Environment Signal Specific Headers", env: map[string]string{ "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) { 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"}), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, map[string]string{"m1": "mv1"}, c.Metrics.Headers) }, }, // Compression Tests { name: "Test With Compression", opts: []otlpconfig.GenericOption{ otlpconfig.WithCompression(otlpconfig.GzipCompression), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, otlpconfig.GzipCompression, c.Metrics.Compression) }, }, { name: "Test Environment Compression", 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) }, }, { name: "Test Environment Signal Specific Compression", 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) }, }, { name: "Test Mixed Environment and With Compression", opts: []otlpconfig.GenericOption{ otlpconfig.WithCompression(otlpconfig.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) }, }, // Timeout Tests { name: "Test With Timeout", opts: []otlpconfig.GenericOption{ otlpconfig.WithTimeout(time.Duration(5 * time.Second)), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, 5*time.Second, c.Metrics.Timeout) }, }, { name: "Test Environment Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 15*time.Second) }, }, { name: "Test Environment Signal Specific Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 28*time.Second) }, }, { name: "Test Mixed Environment and With Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", }, opts: []otlpconfig.GenericOption{ otlpconfig.WithTimeout(5 * time.Second), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Metrics.Timeout, 5*time.Second) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { e := otlpconfig.EnvOptionsReader{ GetEnv: tt.env.getEnv, ReadFile: tt.fileReader.readFile, } // Tests Generic options as HTTP Options cfg := otlpconfig.NewDefaultConfig() e.ApplyHTTPEnvConfigs(&cfg) for _, opt := range tt.opts { opt.ApplyHTTPOption(&cfg) } tt.asserts(t, &cfg, false) // Tests Generic options as gRPC Options cfg = otlpconfig.NewDefaultConfig() e.ApplyGRPCEnvConfigs(&cfg) for _, opt := range tt.opts { opt.ApplyGRPCOption(&cfg) } tt.asserts(t, &cfg, true) }) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/optiontypes.go000066400000000000000000000040771414226744000327260ustar00rootroot00000000000000// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" import "time" const ( // DefaultCollectorPort is the port the Exporter will attempt connect to // if no collector port is provided. DefaultCollectorPort uint16 = 4317 // DefaultCollectorHost is the host address the Exporter will attempt // connect to if no collector address is provided. DefaultCollectorHost string = "localhost" ) // Compression describes the compression used for payloads sent to the // collector. type Compression int const ( // NoCompression tells the driver to send payloads without // compression. NoCompression Compression = iota // GzipCompression tells the driver to send payloads after // compressing them with gzip. GzipCompression ) // RetrySettings defines configuration for retrying batches in case of export failure // using an exponential backoff. type RetrySettings struct { // Enabled indicates whether to not retry sending batches in case of export failure. Enabled bool // InitialInterval the time to wait after the first failure before retrying. InitialInterval time.Duration // MaxInterval is the upper bound on backoff interval. Once this value is reached the delay between // consecutive retries will always be `MaxInterval`. MaxInterval time.Duration // MaxElapsedTime is the maximum amount of time (including retries) spent trying to send a request/batch. // Once this value is reached, the data is discarded. MaxElapsedTime time.Duration } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpconfig/tls.go000066400000000000000000000025141414226744000311250ustar00rootroot00000000000000// 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 otlpconfig import ( "crypto/tls" "crypto/x509" "errors" "io/ioutil" ) // ReadTLSConfigFromFile reads a PEM certificate file and creates // a tls.Config that will use this certifate to verify a server certificate. func ReadTLSConfigFromFile(path string) (*tls.Config, error) { b, err := ioutil.ReadFile(path) if err != nil { return nil, err } return CreateTLSConfig(b) } // CreateTLSConfig creates a tls.Config from a raw certificate bytes // to verify a server certificate. func CreateTLSConfig(certBytes []byte) (*tls.Config, 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 } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpmetrictest/000077500000000000000000000000001414226744000307105ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpmetrictest/client.go000066400000000000000000000062441414226744000325230ustar00rootroot00000000000000// 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 otlpmetrictest import ( "context" "errors" "sync" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" ) func RunExporterShutdownTest(t *testing.T, factory func() otlpmetric.Client) { t.Run("testClientStopHonorsTimeout", func(t *testing.T) { testClientStopHonorsTimeout(t, factory()) }) t.Run("testClientStopHonorsCancel", func(t *testing.T) { testClientStopHonorsCancel(t, factory()) }) t.Run("testClientStopNoError", func(t *testing.T) { testClientStopNoError(t, factory()) }) t.Run("testClientStopManyTimes", func(t *testing.T) { testClientStopManyTimes(t, factory()) }) } func initializeExporter(t *testing.T, client otlpmetric.Client) *otlpmetric.Exporter { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e, err := otlpmetric.New(ctx, client) if err != nil { t.Fatalf("failed to create exporter") } return e } func testClientStopHonorsTimeout(t *testing.T, client otlpmetric.Client) { e := initializeExporter(t, client) innerCtx, innerCancel := context.WithTimeout(context.Background(), time.Microsecond) <-innerCtx.Done() if err := e.Shutdown(innerCtx); err == nil { t.Error("expected context DeadlineExceeded error, got nil") } else if !errors.Is(err, context.DeadlineExceeded) { t.Errorf("expected context DeadlineExceeded error, got %v", err) } innerCancel() } func testClientStopHonorsCancel(t *testing.T, client otlpmetric.Client) { e := initializeExporter(t, client) ctx, innerCancel := context.WithCancel(context.Background()) innerCancel() if err := e.Shutdown(ctx); err == nil { t.Error("expected context canceled error, got nil") } else if !errors.Is(err, context.Canceled) { t.Errorf("expected context canceled error, got %v", err) } } func testClientStopNoError(t *testing.T, client otlpmetric.Client) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e := initializeExporter(t, client) if err := e.Shutdown(ctx); err != nil { t.Errorf("shutdown errored: expected nil, got %v", err) } } func testClientStopManyTimes(t *testing.T, client otlpmetric.Client) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e := initializeExporter(t, client) ch := make(chan struct{}) wg := sync.WaitGroup{} const num int = 20 wg.Add(num) errs := make([]error, num) for i := 0; i < num; i++ { go func(idx int) { defer wg.Done() <-ch errs[idx] = e.Shutdown(ctx) }(i) } close(ch) wg.Wait() for _, err := range errs { if err != nil { t.Fatalf("failed to shutdown exporter: %v", err) } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpmetrictest/collector.go000066400000000000000000000035101414226744000332240ustar00rootroot00000000000000// 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 otlpmetrictest import ( collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) // Collector is an interface that mock collectors should implements, // so they can be used for the end-to-end testing. type Collector interface { Stop() error GetMetrics() []*metricpb.Metric } // MetricsStorage stores the metrics. Mock collectors could use it to // store metrics they have received. type MetricsStorage struct { metrics []*metricpb.Metric } // NewMetricsStorage creates a new metrics storage. func NewMetricsStorage() MetricsStorage { return MetricsStorage{} } // AddMetrics adds metrics to the metrics storage. func (s *MetricsStorage) AddMetrics(request *collectormetricpb.ExportMetricsServiceRequest) { for _, rm := range request.GetResourceMetrics() { // TODO (rghetia) handle multiple resource and library info. if len(rm.InstrumentationLibraryMetrics) > 0 { s.metrics = append(s.metrics, rm.InstrumentationLibraryMetrics[0].Metrics...) } } } // GetMetrics returns the stored metrics. func (s *MetricsStorage) GetMetrics() []*metricpb.Metric { // copy in order to not change. m := make([]*metricpb.Metric, 0, len(s.metrics)) return append(m, s.metrics...) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpmetrictest/data.go000066400000000000000000000045621414226744000321570ustar00rootroot00000000000000// 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 otlpmetrictest import ( "context" "fmt" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" exportmetric "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" ) // OneRecordReader is a Reader that returns just one // filled record. It may be useful for testing driver's metrics // export. func OneRecordReader() exportmetric.InstrumentationLibraryReader { desc := metrictest.NewDescriptor( "foo", sdkapi.CounterInstrumentKind, number.Int64Kind, ) agg := sum.New(1) if err := agg[0].Update(context.Background(), number.NewInt64Number(42), &desc); err != nil { panic(err) } start := time.Date(2020, time.December, 8, 19, 15, 0, 0, time.UTC) end := time.Date(2020, time.December, 8, 19, 16, 0, 0, time.UTC) labels := attribute.NewSet(attribute.String("abc", "def"), attribute.Int64("one", 1)) rec := exportmetric.NewRecord(&desc, &labels, agg[0].Aggregation(), start, end) return processortest.MultiInstrumentationLibraryReader( map[instrumentation.Library][]exportmetric.Record{ { Name: "onelib", }: {rec}, }) } func EmptyReader() exportmetric.InstrumentationLibraryReader { return processortest.MultiInstrumentationLibraryReader(nil) } // FailReader is a checkpointer that returns an error during // ForEach. type FailReader struct{} var _ exportmetric.InstrumentationLibraryReader = FailReader{} // ForEach implements exportmetric.Reader. It always fails. func (FailReader) ForEach(readerFunc func(instrumentation.Library, exportmetric.Reader) error) error { return fmt.Errorf("fail") } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go000066400000000000000000000137711414226744000331260ustar00rootroot00000000000000// 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 otlpmetrictest import ( "context" "fmt" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) // RunEndToEndTest can be used by protocol driver tests to validate // themselves. func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlpmetric.Exporter, mcMetrics Collector) { selector := simple.NewWithInexpensiveDistribution() proc := processor.NewFactory(selector, aggregation.StatelessTemporalitySelector()) cont := controller.New(proc, controller.WithExporter(exp)) require.NoError(t, cont.Start(ctx)) meter := cont.Meter("test-meter") labels := []attribute.KeyValue{attribute.Bool("test", true)} type data struct { iKind sdkapi.InstrumentKind nKind number.Kind val int64 } instruments := map[string]data{ "test-int64-counter": {sdkapi.CounterInstrumentKind, number.Int64Kind, 1}, "test-float64-counter": {sdkapi.CounterInstrumentKind, number.Float64Kind, 1}, "test-int64-histogram": {sdkapi.HistogramInstrumentKind, number.Int64Kind, 2}, "test-float64-histogram": {sdkapi.HistogramInstrumentKind, number.Float64Kind, 2}, "test-int64-gaugeobserver": {sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, 3}, "test-float64-gaugeobserver": {sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, 3}, } for name, data := range instruments { data := data switch data.iKind { case sdkapi.CounterInstrumentKind: switch data.nKind { case number.Int64Kind: metric.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels...) case number.Float64Kind: metric.Must(meter).NewFloat64Counter(name).Add(ctx, float64(data.val), labels...) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } case sdkapi.HistogramInstrumentKind: switch data.nKind { case number.Int64Kind: metric.Must(meter).NewInt64Histogram(name).Record(ctx, data.val, labels...) case number.Float64Kind: metric.Must(meter).NewFloat64Histogram(name).Record(ctx, float64(data.val), labels...) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } case sdkapi.GaugeObserverInstrumentKind: switch data.nKind { case number.Int64Kind: metric.Must(meter).NewInt64GaugeObserver(name, func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(data.val, labels...) }, ) case number.Float64Kind: callback := func(v float64) metric.Float64ObserverFunc { return metric.Float64ObserverFunc(func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(v, labels...) }) }(float64(data.val)) metric.Must(meter).NewFloat64GaugeObserver(name, callback) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } default: assert.Failf(t, "unsupported metrics testing kind", data.iKind.String()) } } // Flush and close. require.NoError(t, cont.Stop(ctx)) // Wait >2 cycles. <-time.After(40 * time.Millisecond) // Now shutdown the exporter ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { t.Fatalf("failed to stop the exporter: %v", err) } // Shutdown the collector too so that we can begin // verification checks of expected data back. _ = mcMetrics.Stop() metrics := mcMetrics.GetMetrics() assert.Len(t, metrics, len(instruments), "not enough metrics exported") seen := make(map[string]struct{}, len(instruments)) for _, m := range metrics { data, ok := instruments[m.Name] if !ok { assert.Failf(t, "unknown metrics", m.Name) continue } seen[m.Name] = struct{}{} switch data.iKind { case sdkapi.CounterInstrumentKind, sdkapi.GaugeObserverInstrumentKind: var dp []*metricpb.NumberDataPoint switch data.iKind { case sdkapi.CounterInstrumentKind: require.NotNil(t, m.GetSum()) dp = m.GetSum().GetDataPoints() case sdkapi.GaugeObserverInstrumentKind: require.NotNil(t, m.GetGauge()) dp = m.GetGauge().GetDataPoints() } if assert.Len(t, dp, 1) { switch data.nKind { case number.Int64Kind: v := &metricpb.NumberDataPoint_AsInt{AsInt: data.val} assert.Equal(t, v, dp[0].Value, "invalid value for %q", m.Name) case number.Float64Kind: v := &metricpb.NumberDataPoint_AsDouble{AsDouble: float64(data.val)} assert.Equal(t, v, dp[0].Value, "invalid value for %q", m.Name) } } case sdkapi.HistogramInstrumentKind: require.NotNil(t, m.GetSummary()) if dp := m.GetSummary().DataPoints; assert.Len(t, dp, 1) { count := dp[0].Count assert.Equal(t, uint64(1), count, "invalid count for %q", m.Name) assert.Equal(t, float64(data.val*int64(count)), dp[0].Sum, "invalid sum for %q (value %d)", m.Name, data.val) } default: assert.Failf(t, "invalid metrics kind", data.iKind.String()) } } for i := range instruments { if _, ok := seen[i]; !ok { assert.Fail(t, fmt.Sprintf("no metric(s) exported for %q", i)) } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/options.go000066400000000000000000000027121414226744000260360ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" import "go.opentelemetry.io/otel/sdk/export/metric/aggregation" // Option are setting options passed to an Exporter on creation. type Option interface { apply(*config) } type exporterOptionFunc func(*config) func (fn exporterOptionFunc) apply(cfg *config) { fn(cfg) } type config struct { temporalitySelector aggregation.TemporalitySelector } // WithMetricAggregationTemporalitySelector defines the aggregation.TemporalitySelector used // for selecting aggregation.Temporality (i.e., Cumulative vs. Delta // aggregation). If not specified otherwise, exporter will use a // cumulative temporality selector. func WithMetricAggregationTemporalitySelector(selector aggregation.TemporalitySelector) Option { return exporterOptionFunc(func(cfg *config) { cfg.temporalitySelector = selector }) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/000077500000000000000000000000001414226744000270505ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go000066400000000000000000000060121414226744000306540ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" import ( "context" "errors" "fmt" "sync" "google.golang.org/grpc" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/connection" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) type client struct { connection *connection.Connection lock sync.Mutex metricsClient colmetricpb.MetricsServiceClient } var ( errNoClient = errors.New("no client") ) // NewClient creates a new gRPC metric client. func NewClient(opts ...Option) otlpmetric.Client { cfg := otlpconfig.NewDefaultConfig() otlpconfig.ApplyGRPCEnvConfigs(&cfg) for _, opt := range opts { opt.applyGRPCOption(&cfg) } c := &client{} c.connection = connection.NewConnection(cfg, cfg.Metrics, c.handleNewConnection) return c } func (c *client) handleNewConnection(cc *grpc.ClientConn) { c.lock.Lock() defer c.lock.Unlock() if cc != nil { c.metricsClient = colmetricpb.NewMetricsServiceClient(cc) } else { c.metricsClient = nil } } // Start establishes a connection to the collector. func (c *client) Start(ctx context.Context) error { return c.connection.StartConnection(ctx) } // Stop shuts down the connection to the collector. func (c *client) Stop(ctx context.Context) error { return c.connection.Shutdown(ctx) } // UploadMetrics sends a batch of metrics to the collector. func (c *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error { if !c.connection.Connected() { return fmt.Errorf("metrics exporter is disconnected from the server %s: %w", c.connection.SCfg.Endpoint, c.connection.LastConnectError()) } ctx, cancel := c.connection.ContextWithStop(ctx) defer cancel() ctx, tCancel := context.WithTimeout(ctx, c.connection.SCfg.Timeout) defer tCancel() ctx = c.connection.ContextWithMetadata(ctx) err := func() error { c.lock.Lock() defer c.lock.Unlock() if c.metricsClient == nil { return errNoClient } return c.connection.DoRequest(ctx, func(ctx context.Context) error { _, err := c.metricsClient.Export(ctx, &colmetricpb.ExportMetricsServiceRequest{ ResourceMetrics: protoMetrics, }) return err }) }() if err != nil { c.connection.SetStateDisconnected(err) } return err } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go000066400000000000000000000524321414226744000317220ustar00rootroot00000000000000// 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" "fmt" "net" "strings" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/sdk/resource" "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/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" ) var ( oneRecord = otlpmetrictest.OneRecordReader() testResource = resource.Empty() ) func TestNewExporter_endToEnd(t *testing.T) { tests := []struct { name string additionalOpts []otlpmetricgrpc.Option }{ { name: "StandardExporter", }, { name: "WithCompressor", additionalOpts: []otlpmetricgrpc.Option{ otlpmetricgrpc.WithCompressor(gzip.Name), }, }, { name: "WithServiceConfig", additionalOpts: []otlpmetricgrpc.Option{ otlpmetricgrpc.WithServiceConfig("{}"), }, }, { name: "WithDialOptions", additionalOpts: []otlpmetricgrpc.Option{ otlpmetricgrpc.WithDialOption(grpc.WithBlock()), }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { newExporterEndToEndTest(t, test.additionalOpts) }) } } func newGRPCExporter(t *testing.T, ctx context.Context, endpoint string, additionalOpts ...otlpmetricgrpc.Option) *otlpmetric.Exporter { opts := []otlpmetricgrpc.Option{ otlpmetricgrpc.WithInsecure(), otlpmetricgrpc.WithEndpoint(endpoint), otlpmetricgrpc.WithReconnectionPeriod(50 * time.Millisecond), } opts = append(opts, additionalOpts...) client := otlpmetricgrpc.NewClient(opts...) exp, err := otlpmetric.New(ctx, client) if err != nil { t.Fatalf("failed to create a new collector exporter: %v", err) } return exp } func newExporterEndToEndTest(t *testing.T, additionalOpts []otlpmetricgrpc.Option) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, additionalOpts...) defer func() { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { panic(err) } }() otlpmetrictest.RunEndToEndTest(ctx, t, exp, mc) } func TestExporterShutdown(t *testing.T) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.Stop() }() <-time.After(5 * time.Millisecond) otlpmetrictest.RunExporterShutdownTest(t, func() otlpmetric.Client { return otlpmetricgrpc.NewClient( otlpmetricgrpc.WithInsecure(), otlpmetricgrpc.WithEndpoint(mc.endpoint), otlpmetricgrpc.WithReconnectionPeriod(50*time.Millisecond), ) }) } func TestNewExporter_invokeStartThenStopManyTimes(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { if err := exp.Shutdown(ctx); err != nil { panic(err) } }() // Invoke Start numerous times, should return errAlreadyStarted for i := 0; i < 10; i++ { if err := exp.Start(ctx); err == nil || !strings.Contains(err.Error(), "already started") { t.Fatalf("#%d unexpected Start error: %v", i, err) } } if err := exp.Shutdown(ctx); err != nil { t.Fatalf("failed to Shutdown the exporter: %v", err) } // Invoke Shutdown numerous times for i := 0; i < 10; i++ { if err := exp.Shutdown(ctx); err != nil { t.Fatalf(`#%d got error (%v) expected none`, i, err) } } } func TestNewExporter_collectorConnectionDiesThenReconnectsWhenInRestMode(t *testing.T) { mc := runMockCollector(t) reconnectionPeriod := 20 * time.Millisecond ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false}), otlpmetricgrpc.WithReconnectionPeriod(reconnectionPeriod)) defer func() { require.NoError(t, exp.Shutdown(ctx)) }() // Wait for a connection. mc.ln.WaitForConn() // We'll now stop the collector right away to simulate a connection // dying in the midst of communication or even not existing before. require.NoError(t, mc.stop()) // first export, it will send disconnected message to the channel on export failure, // trigger almost immediate reconnection require.Error(t, exp.Export(ctx, testResource, oneRecord)) // second export, it will detect connection issue, change state of exporter to disconnected and // send message to disconnected channel but this time reconnection gouroutine will be in (rest mode, not listening to the disconnected channel) require.Error(t, exp.Export(ctx, testResource, oneRecord)) // as a result we have exporter in disconnected state waiting for disconnection message to reconnect // resurrect collector nmc := runMockCollectorAtEndpoint(t, mc.endpoint) // make sure reconnection loop hits beginning and goes back to waiting mode // after hitting beginning of the loop it should reconnect nmc.ln.WaitForConn() n := 10 for i := 0; i < n; i++ { // when disconnected exp.Export doesnt send disconnected messages again // it just quits and return last connection error require.NoError(t, exp.Export(ctx, testResource, oneRecord)) } nmaMetrics := nmc.getMetrics() if g, w := len(nmaMetrics), n; g != w { t.Fatalf("Connected collector: metrics: got %d want %d", g, w) } dMetrics := mc.getMetrics() // Expecting 0 metrics to have been received by the original but now dead collector if g, w := len(dMetrics), 0; g != w { t.Fatalf("Disconnected collector: spans: got %d want %d", g, w) } require.NoError(t, nmc.Stop()) } func TestExporterExportFailureAndRecoveryModes(t *testing.T) { tts := []struct { name string errors []error rs otlpmetricgrpc.RetrySettings fn func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) opts []otlpmetricgrpc.Option }{ { name: "Do not retry if succeeded", fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { require.NoError(t, exp.Export(ctx, testResource, oneRecord)) metrics := mc.getMetrics() require.Len(t, metrics, 1) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 success request.") }, }, { name: "Do not retry if 'error' is ok", errors: []error{ status.Error(codes.OK, ""), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { require.NoError(t, exp.Export(ctx, testResource, oneRecord)) metrics := mc.getMetrics() require.Len(t, metrics, 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 error OK request.") }, }, { name: "Fail three times and succeed", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: 300 * time.Millisecond, InitialInterval: 2 * time.Millisecond, MaxInterval: 10 * time.Millisecond, }, errors: []error{ status.Error(codes.Unavailable, "backend under pressure"), status.Error(codes.Unavailable, "backend under pressure"), status.Error(codes.Unavailable, "backend under pressure"), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { require.NoError(t, exp.Export(ctx, testResource, oneRecord)) metrics := mc.getMetrics() require.Len(t, metrics, 1) require.Equal(t, 4, mc.metricSvc.requests, "metric service must receive 3 failure requests and 1 success request.") }, }, { name: "Permanent error should not be retried", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: 300 * time.Millisecond, InitialInterval: 2 * time.Millisecond, MaxInterval: 10 * time.Millisecond, }, errors: []error{ status.Error(codes.InvalidArgument, "invalid arguments"), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { require.Error(t, exp.Export(ctx, testResource, oneRecord)) metric := mc.getMetrics() require.Len(t, metric, 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 error requests.") }, }, { name: "Test all transient errors and succeed", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: 500 * time.Millisecond, InitialInterval: 1 * time.Millisecond, MaxInterval: 2 * time.Millisecond, }, errors: []error{ status.Error(codes.Canceled, ""), status.Error(codes.DeadlineExceeded, ""), status.Error(codes.ResourceExhausted, ""), status.Error(codes.Aborted, ""), status.Error(codes.OutOfRange, ""), status.Error(codes.Unavailable, ""), status.Error(codes.DataLoss, ""), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { require.NoError(t, exp.Export(ctx, testResource, oneRecord)) metrics := mc.getMetrics() require.Len(t, metrics, 1) require.Equal(t, 8, mc.metricSvc.requests, "metric service must receive 9 failure requests and 1 success request.") }, }, { name: "Retry should honor server throttling", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: time.Minute, InitialInterval: time.Nanosecond, MaxInterval: time.Nanosecond, }, opts: []otlpmetricgrpc.Option{ otlpmetricgrpc.WithTimeout(time.Millisecond * 100), }, errors: []error{ newThrottlingError(codes.ResourceExhausted, time.Second*30), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { err := exp.Export(ctx, testResource, oneRecord) require.Error(t, err) require.Equal(t, "context deadline exceeded", err.Error()) metrics := mc.getMetrics() require.Len(t, metrics, 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests and 1 success request.") }, }, { name: "Retry should fail if server throttling is higher than the MaxElapsedTime", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: time.Millisecond * 100, InitialInterval: time.Nanosecond, MaxInterval: time.Nanosecond, }, errors: []error{ newThrottlingError(codes.ResourceExhausted, time.Minute), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { err := exp.Export(ctx, testResource, oneRecord) require.Error(t, err) require.Equal(t, "max elapsed time expired when respecting server throttle: rpc error: code = ResourceExhausted desc = ", err.Error()) metrics := mc.getMetrics() require.Len(t, metrics, 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests and 1 success request.") }, }, { name: "Retry stops if takes too long", rs: otlpmetricgrpc.RetrySettings{ Enabled: true, MaxElapsedTime: time.Millisecond * 100, InitialInterval: time.Millisecond * 50, MaxInterval: time.Millisecond * 50, }, errors: []error{ status.Error(codes.Unavailable, "unavailable"), status.Error(codes.Unavailable, "unavailable"), status.Error(codes.Unavailable, "unavailable"), status.Error(codes.Unavailable, "unavailable"), status.Error(codes.Unavailable, "unavailable"), status.Error(codes.Unavailable, "unavailable"), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { err := exp.Export(ctx, testResource, oneRecord) require.Error(t, err) require.Equal(t, "max elapsed time expired: rpc error: code = Unavailable desc = unavailable", err.Error()) metrics := mc.getMetrics() require.Len(t, metrics, 0) require.LessOrEqual(t, 1, mc.metricSvc.requests, "metric service must receive at least 1 failure requests.") }, }, { name: "Disabled retry", rs: otlpmetricgrpc.RetrySettings{ Enabled: false, }, errors: []error{ status.Error(codes.Unavailable, "unavailable"), }, fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { err := exp.Export(ctx, testResource, oneRecord) require.Error(t, err) require.Equal(t, "rpc error: code = Unavailable desc = unavailable", err.Error()) metrics := mc.getMetrics() require.Len(t, metrics, 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 failure requests.") }, }, } for _, tt := range tts { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mc := runMockCollectorWithConfig(t, &mockConfig{ errors: tt.errors, }) opts := []otlpmetricgrpc.Option{ otlpmetricgrpc.WithRetry(tt.rs), } if len(tt.opts) != 0 { opts = append(opts, tt.opts...) } exp := newGRPCExporter(t, ctx, mc.endpoint, opts...) tt.fn(t, ctx, exp, mc) require.NoError(t, mc.Stop()) require.NoError(t, exp.Shutdown(ctx)) }) } } func TestPermanentErrorsShouldNotBeRetried(t *testing.T) { permanentErrors := []*status.Status{ status.New(codes.Unknown, "Unknown"), status.New(codes.InvalidArgument, "InvalidArgument"), status.New(codes.NotFound, "NotFound"), status.New(codes.AlreadyExists, "AlreadyExists"), status.New(codes.FailedPrecondition, "FailedPrecondition"), status.New(codes.Unimplemented, "Unimplemented"), status.New(codes.Internal, "Internal"), status.New(codes.PermissionDenied, ""), status.New(codes.Unauthenticated, ""), } for _, sts := range permanentErrors { t.Run(sts.Code().String(), func(t *testing.T) { ctx := context.Background() mc := runMockCollectorWithConfig(t, &mockConfig{ errors: []error{sts.Err()}, }) exp := newGRPCExporter(t, ctx, mc.endpoint) err := exp.Export(ctx, testResource, oneRecord) require.Error(t, err) require.Len(t, mc.getMetrics(), 0) require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 permanent error requests.") require.NoError(t, mc.Stop()) require.NoError(t, exp.Shutdown(ctx)) }) } } func newThrottlingError(code codes.Code, duration time.Duration) error { s := status.New(code, "") s, _ = s.WithDetails(&errdetails.RetryInfo{RetryDelay: durationpb.New(duration)}) return s.Err() } func TestNewExporter_collectorConnectionDiesThenReconnects(t *testing.T) { mc := runMockCollector(t) reconnectionPeriod := 50 * time.Millisecond ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false}), otlpmetricgrpc.WithReconnectionPeriod(reconnectionPeriod)) defer func() { require.NoError(t, exp.Shutdown(ctx)) }() mc.ln.WaitForConn() // We'll now stop the collector right away to simulate a connection // dying in the midst of communication or even not existing before. require.NoError(t, mc.stop()) // In the test below, we'll stop the collector many times, // while exporting metrics and test to ensure that we can // reconnect. for j := 0; j < 3; j++ { // No endpoint up. require.Error(t, exp.Export(ctx, testResource, oneRecord)) // Now resurrect the collector by making a new one but reusing the // old endpoint, and the collector should reconnect automatically. nmc := runMockCollectorAtEndpoint(t, mc.endpoint) // Give the exporter sometime to reconnect nmc.ln.WaitForConn() n := 10 for i := 0; i < n; i++ { require.NoError(t, exp.Export(ctx, testResource, oneRecord)) } nmaMetrics := nmc.getMetrics() // Expecting 10 metrics that were sampled, given that if g, w := len(nmaMetrics), n; g != w { t.Fatalf("Round #%d: Connected collector: spans: got %d want %d", j, g, w) } dMetrics := mc.getMetrics() // Expecting 0 metrics to have been received by the original but now dead collector if g, w := len(dMetrics), 0; g != w { t.Fatalf("Round #%d: Disconnected collector: spans: got %d want %d", j, g, w) } // Disconnect for the next try. require.NoError(t, nmc.stop()) } } // This test takes a long time to run: to skip it, run tests using: -short func TestNewExporter_collectorOnBadConnection(t *testing.T) { if testing.Short() { t.Skipf("Skipping this long running test") } ln, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to grab an available port: %v", err) } // Firstly close the "collector's" channel: optimistically this endpoint won't get reused ASAP // However, our goal of closing it is to simulate an unavailable connection _ = ln.Close() _, collectorPortStr, _ := net.SplitHostPort(ln.Addr().String()) endpoint := fmt.Sprintf("localhost:%s", collectorPortStr) ctx := context.Background() exp := newGRPCExporter(t, ctx, endpoint) _ = exp.Shutdown(ctx) } func TestNewExporter_withEndpoint(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) _ = exp.Shutdown(ctx) } func TestNewExporter_withHeaders(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithHeaders(map[string]string{"header1": "value1"})) require.NoError(t, exp.Export(ctx, testResource, oneRecord)) defer func() { _ = exp.Shutdown(ctx) }() headers := mc.getHeaders() require.Len(t, headers.Get("header1"), 1) assert.Equal(t, "value1", headers.Get("header1")[0]) } func TestNewExporter_WithTimeout(t *testing.T) { tts := []struct { name string fn func(exp *otlpmetric.Exporter) error timeout time.Duration metrics int spans int code codes.Code delay bool }{ { name: "Timeout Metrics", fn: func(exp *otlpmetric.Exporter) error { return exp.Export(context.Background(), testResource, oneRecord) }, timeout: time.Millisecond * 100, code: codes.DeadlineExceeded, delay: true, }, { name: "No Timeout Metrics", fn: func(exp *otlpmetric.Exporter) error { return exp.Export(context.Background(), testResource, oneRecord) }, timeout: time.Minute, metrics: 1, code: codes.OK, }, } for _, tt := range tts { t.Run(tt.name, func(t *testing.T) { mc := runMockCollector(t) if tt.delay { mc.metricSvc.delay = time.Second * 10 } defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlpmetricgrpc.WithTimeout(tt.timeout), otlpmetricgrpc.WithRetry(otlpmetricgrpc.RetrySettings{Enabled: false})) defer func() { _ = exp.Shutdown(ctx) }() err := tt.fn(exp) if tt.code == codes.OK { require.NoError(t, err) } else { require.Error(t, err) } s := status.Convert(err) require.Equal(t, tt.code, s.Code()) require.Len(t, mc.getMetrics(), tt.metrics) }) } } func TestNewExporter_withInvalidSecurityConfiguration(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() client := otlpmetricgrpc.NewClient(otlpmetricgrpc.WithEndpoint(mc.endpoint)) exp, err := otlpmetric.New(ctx, client) if err != nil { t.Fatalf("failed to create a new collector exporter: %v", err) } err = exp.Export(ctx, testResource, oneRecord) expectedErr := fmt.Sprintf("metrics exporter is disconnected from the server %s: grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)", mc.endpoint) require.Error(t, err) require.Equal(t, expectedErr, err.Error()) defer func() { _ = exp.Shutdown(ctx) }() } func TestDisconnected(t *testing.T) { ctx := context.Background() // The endpoint is whatever, we want to be disconnected. But we // setting a blocking connection, so dialing to the invalid // endpoint actually fails. exp := newGRPCExporter(t, ctx, "invalid", otlpmetricgrpc.WithReconnectionPeriod(time.Hour), otlpmetricgrpc.WithDialOption( grpc.WithBlock(), grpc.FailOnNonTempDialError(true), ), ) defer func() { assert.NoError(t, exp.Shutdown(ctx)) }() assert.Error(t, exp.Export(ctx, testResource, oneRecord)) } func TestEmptyData(t *testing.T) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { assert.NoError(t, exp.Shutdown(ctx)) }() assert.NoError(t, exp.Export(ctx, testResource, otlpmetrictest.EmptyReader())) } func TestFailedMetricTransform(t *testing.T) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { assert.NoError(t, exp.Shutdown(ctx)) }() assert.Error(t, exp.Export(ctx, testResource, otlpmetrictest.FailReader{})) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go000066400000000000000000000124041414226744000320720ustar00rootroot00000000000000// 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" "log" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/sdk/metric/selector/simple" "google.golang.org/grpc/credentials" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/global" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" ) func Example_insecure() { ctx := context.Background() client := otlpmetricgrpc.NewClient(otlpmetricgrpc.WithInsecure()) exp, err := otlpmetric.New(ctx, client) if err != nil { log.Fatalf("Failed to create the collector exporter: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { otel.Handle(err) } }() pusher := controller.New( processor.NewFactory( simple.NewWithExactDistribution(), exp, ), controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() // pushes any last exports to the receiver if err := pusher.Stop(ctx); err != nil { otel.Handle(err) } }() meter := global.Meter("test-meter") // Recorder metric example counter := metric.Must(meter). NewFloat64Counter( "an_important_metric", metric.WithDescription("Measures the cumulative epicness of the app"), ) for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) counter.Add(ctx, 1.0) } } func Example_withTLS() { // Please take at look at https://pkg.go.dev/google.golang.org/grpc/credentials#TransportCredentials // for ways on how to initialize gRPC TransportCredentials. creds, err := credentials.NewClientTLSFromFile("my-cert.pem", "") if err != nil { log.Fatalf("failed to create gRPC client TLS credentials: %v", err) } ctx := context.Background() client := otlpmetricgrpc.NewClient(otlpmetricgrpc.WithTLSCredentials(creds)) exp, err := otlpmetric.New(ctx, client) if err != nil { log.Fatalf("failed to create the collector exporter: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { otel.Handle(err) } }() pusher := controller.New( processor.NewFactory( simple.NewWithExactDistribution(), exp, ), controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() // pushes any last exports to the receiver if err := pusher.Stop(ctx); err != nil { otel.Handle(err) } }() meter := global.Meter("test-meter") // Recorder metric example counter := metric.Must(meter). NewFloat64Counter( "an_important_metric", metric.WithDescription("Measures the cumulative epicness of the app"), ) for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) counter.Add(ctx, 1.0) } } func Example_withDifferentSignalCollectors() { client := otlpmetricgrpc.NewClient( otlpmetricgrpc.WithInsecure(), otlpmetricgrpc.WithEndpoint("localhost:30080"), ) ctx := context.Background() exp, err := otlpmetric.New(ctx, client) if err != nil { log.Fatalf("failed to create the collector exporter: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { otel.Handle(err) } }() pusher := controller.New( processor.NewFactory( simple.NewWithExactDistribution(), exp, ), controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) } defer func() { ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() // pushes any last exports to the receiver if err := pusher.Stop(ctx); err != nil { otel.Handle(err) } }() meter := global.Meter("test-meter") // Recorder metric example counter := metric.Must(meter). NewFloat64Counter( "an_important_metric", metric.WithDescription("Measures the cumulative epicness of the app"), ) for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) counter.Add(ctx, 1.0) } log.Printf("Done!") } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/exporter.go000066400000000000000000000021531414226744000312500ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" import ( "context" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" ) // New constructs a new Exporter and starts it. func New(ctx context.Context, opts ...Option) (*otlpmetric.Exporter, error) { return otlpmetric.New(ctx, NewClient(opts...)) } // NewUnstarted constructs a new Exporter and does not start it. func NewUnstarted(opts ...Option) *otlpmetric.Exporter { return otlpmetric.NewUnstarted(NewClient(opts...)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod000066400000000000000000000061361414226744000301640ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc go 1.15 require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.24.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.41.0 google.golang.org/protobuf v1.27.1 ) replace go.opentelemetry.io/otel => ../../../.. replace go.opentelemetry.io/otel/sdk => ../../../../sdk replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric replace go.opentelemetry.io/otel/exporters/otlp => ../.. replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../ replace go.opentelemetry.io/otel/metric => ../../../../metric replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ./ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/go.sum000066400000000000000000000317041414226744000302100ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/mock_collector_test.go000066400000000000000000000132171414226744000334410ustar00rootroot00000000000000// 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" "fmt" "net" "runtime" "strings" "sync" "testing" "time" "google.golang.org/grpc/metadata" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" "google.golang.org/grpc" collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) func makeMockCollector(t *testing.T, mockConfig *mockConfig) *mockCollector { return &mockCollector{ t: t, metricSvc: &mockMetricService{ storage: otlpmetrictest.NewMetricsStorage(), errors: mockConfig.errors, }, } } type mockMetricService struct { collectormetricpb.UnimplementedMetricsServiceServer requests int errors []error headers metadata.MD mu sync.RWMutex storage otlpmetrictest.MetricsStorage delay time.Duration } func (mms *mockMetricService) getHeaders() metadata.MD { mms.mu.RLock() defer mms.mu.RUnlock() return mms.headers } func (mms *mockMetricService) getMetrics() []*metricpb.Metric { mms.mu.RLock() defer mms.mu.RUnlock() return mms.storage.GetMetrics() } func (mms *mockMetricService) Export(ctx context.Context, exp *collectormetricpb.ExportMetricsServiceRequest) (*collectormetricpb.ExportMetricsServiceResponse, error) { if mms.delay > 0 { time.Sleep(mms.delay) } mms.mu.Lock() defer func() { mms.requests++ mms.mu.Unlock() }() reply := &collectormetricpb.ExportMetricsServiceResponse{} if mms.requests < len(mms.errors) { idx := mms.requests return reply, mms.errors[idx] } mms.headers, _ = metadata.FromIncomingContext(ctx) mms.storage.AddMetrics(exp) return reply, nil } type mockCollector struct { t *testing.T metricSvc *mockMetricService endpoint string ln *listener stopFunc func() stopOnce sync.Once } type mockConfig struct { errors []error endpoint string } var _ collectormetricpb.MetricsServiceServer = (*mockMetricService)(nil) var errAlreadyStopped = fmt.Errorf("already stopped") func (mc *mockCollector) stop() error { var err = errAlreadyStopped mc.stopOnce.Do(func() { err = nil if mc.stopFunc != nil { mc.stopFunc() } }) // Give it sometime to shutdown. <-time.After(160 * time.Millisecond) // Wait for services to finish reading/writing. // Getting the lock ensures the metricSvc is done flushing. mc.metricSvc.mu.Lock() defer mc.metricSvc.mu.Unlock() return err } func (mc *mockCollector) Stop() error { return mc.stop() } func (mc *mockCollector) getHeaders() metadata.MD { return mc.metricSvc.getHeaders() } func (mc *mockCollector) getMetrics() []*metricpb.Metric { return mc.metricSvc.getMetrics() } func (mc *mockCollector) GetMetrics() []*metricpb.Metric { return mc.getMetrics() } // runMockCollector is a helper function to create a mock Collector func runMockCollector(t *testing.T) *mockCollector { return runMockCollectorAtEndpoint(t, "localhost:0") } func runMockCollectorAtEndpoint(t *testing.T, endpoint string) *mockCollector { return runMockCollectorWithConfig(t, &mockConfig{endpoint: endpoint}) } func runMockCollectorWithConfig(t *testing.T, mockConfig *mockConfig) *mockCollector { ln, err := net.Listen("tcp", mockConfig.endpoint) if err != nil { t.Fatalf("Failed to get an endpoint: %v", err) } srv := grpc.NewServer() mc := makeMockCollector(t, mockConfig) collectormetricpb.RegisterMetricsServiceServer(srv, mc.metricSvc) mc.ln = newListener(ln) go func() { _ = srv.Serve((net.Listener)(mc.ln)) }() mc.endpoint = ln.Addr().String() // srv.Stop calls Close on mc.ln. mc.stopFunc = srv.Stop return mc } type listener struct { closeOnce sync.Once wrapped net.Listener C chan struct{} } func newListener(wrapped net.Listener) *listener { return &listener{ wrapped: wrapped, C: make(chan struct{}, 1), } } func (l *listener) Close() error { return l.wrapped.Close() } func (l *listener) Addr() net.Addr { return l.wrapped.Addr() } // Accept waits for and returns the next connection to the listener. It will // send a signal on l.C that a connection has been made before returning. func (l *listener) Accept() (net.Conn, error) { conn, err := l.wrapped.Accept() if err != nil { // Go 1.16 exported net.ErrClosed that could clean up this check, but to // remain backwards compatible with previous versions of Go that we // support the following string evaluation is used instead to keep in line // with the previously recommended way to check this: // https://github.com/golang/go/issues/4373#issuecomment-353076799 if strings.Contains(err.Error(), "use of closed network connection") { // If the listener has been closed, do not allow callers of // WaitForConn to wait for a connection that will never come. l.closeOnce.Do(func() { close(l.C) }) } return conn, err } select { case l.C <- struct{}{}: default: // If C is full, assume nobody is listening and move on. } return conn, nil } // WaitForConn will wait indefintely for a connection to be estabilished with // the listener before returning. func (l *listener) WaitForConn() { for { select { case <-l.C: return default: runtime.Gosched() } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetricgrpc/options.go000066400000000000000000000126441414226744000311010ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" import ( "fmt" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "go.opentelemetry.io/otel" ) // Option applies an option to the gRPC client. type Option interface { applyGRPCOption(*otlpconfig.Config) } // RetrySettings defines configuration for retrying batches in case of export failure // using an exponential backoff. type RetrySettings otlpconfig.RetrySettings type wrappedOption struct { otlpconfig.GRPCOption } func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) { 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. Note, by default, client security is required unless WithInsecure is used. func WithInsecure() Option { return wrappedOption{otlpconfig.WithInsecure()} } // WithEndpoint allows one to set the endpoint that the exporter will // connect to the collector on. If unset, it will instead try to use // connect to DefaultCollectorHost:DefaultCollectorPort. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } // WithReconnectionPeriod allows one to set the delay between next connection attempt // after failing to connect with the collector. func WithReconnectionPeriod(rp time.Duration) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.ReconnectionPeriod = rp })} } func compressorToCompression(compressor string) otlpconfig.Compression { switch compressor { case "gzip": return otlpconfig.GzipCompression } otel.Handle(fmt.Errorf("invalid compression type: '%s', using no compression as default", compressor)) return otlpconfig.NoCompression } // WithCompressor will set the compressor for the gRPC client to use when sending requests. // It is the responsibility of the caller to ensure that the compressor set has been registered // with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some // compressors auto-register on import, such as gzip, which can be registered by calling // `import _ "google.golang.org/grpc/encoding/gzip"`. func WithCompressor(compressor string) Option { return wrappedOption{otlpconfig.WithCompression(compressorToCompression(compressor))} } // WithHeaders will send the provided headers with gRPC requests. func WithHeaders(headers map[string]string) Option { return wrappedOption{otlpconfig.WithHeaders(headers)} } // WithTLSCredentials allows the connection to use TLS credentials // when talking to the server. It takes in grpc.TransportCredentials instead // of say a Certificate file or a tls.Certificate, because the retrieving of // these credentials can be done in many ways e.g. plain file, in code tls.Config // or by certificate rotation, so it is up to the caller to decide what to use. func WithTLSCredentials(creds credentials.TransportCredentials) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.Metrics.GRPCCredentials = creds })} } // WithServiceConfig defines the default gRPC service config used. func WithServiceConfig(serviceConfig string) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.ServiceConfig = serviceConfig })} } // WithDialOption opens support to any grpc.DialOption to be used. If it conflicts // with some other configuration the GRPC specified via the collector the ones here will // take preference since they are set last. func WithDialOption(opts ...grpc.DialOption) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.DialOptions = opts })} } // WithGRPCConn allows reusing existing gRPC connection when it has already been // established for other services. When set, other dial options will be ignored. func WithGRPCConn(conn *grpc.ClientConn) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.GRPCConn = conn })} } // WithTimeout tells the client the max waiting time for the backend to process // each metrics batch. If unset, the default will be 10 seconds. func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } // WithRetry configures the retry policy for transient errors that may occurs when // exporting metrics. An exponential back-off algorithm is used to // ensure endpoints are not overwhelmed with retries. If unset, the default // retry policy will retry after 5 seconds and increase exponentially after each // error for a total of 1 minute. func WithRetry(settings RetrySettings) Option { return wrappedOption{otlpconfig.WithRetry(otlpconfig.RetrySettings(settings))} } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/000077500000000000000000000000001414226744000270745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/certificate_test.go000066400000000000000000000051641414226744000327520ustar00rootroot00000000000000// 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 otlpmetrichttp_test import ( "bytes" "crypto/ecdsa" "crypto/elliptic" cryptorand "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" mathrand "math/rand" "net" "time" ) type mathRandReader struct{} func (mathRandReader) Read(p []byte) (n int, err error) { return mathrand.Read(p) } var randReader mathRandReader type pemCertificate struct { Certificate []byte PrivateKey []byte } // Based on https://golang.org/src/crypto/tls/generate_cert.go, // simplified and weakened. func generateWeakCertificate() (*pemCertificate, error) { priv, err := ecdsa.GenerateKey(elliptic.P256(), randReader) if err != nil { return nil, err } keyUsage := x509.KeyUsageDigitalSignature notBefore := time.Now() notAfter := notBefore.Add(time.Hour) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := cryptorand.Int(randReader, serialNumberLimit) if err != nil { return nil, err } template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"otel-go"}, }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: keyUsage, 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, &template, &template, &priv.PublicKey, priv) if err != nil { return nil, err } certificateBuffer := new(bytes.Buffer) if err := pem.Encode(certificateBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { return nil, err } privDERBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { return nil, err } privBuffer := new(bytes.Buffer) if err := pem.Encode(privBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: privDERBytes}); err != nil { return nil, err } return &pemCertificate{ Certificate: certificateBuffer.Bytes(), PrivateKey: privBuffer.Bytes(), }, nil } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/client.go000066400000000000000000000171561414226744000307130ustar00rootroot00000000000000// 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 otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" import ( "bytes" "compress/gzip" "context" "fmt" "io" "io/ioutil" "math/rand" "net" "net/http" "path" "strings" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" "google.golang.org/protobuf/proto" "go.opentelemetry.io/otel" colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" ) const contentTypeProto = "application/x-protobuf" // Keep it in sync with golang's DefaultTransport from net/http! We // have our own copy to avoid handling a situation where the // DefaultTransport is overwritten with some different implementation // of http.RoundTripper or it's modified by other package. var ourTransport = &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } type client struct { name string cfg otlpconfig.SignalConfig generalCfg otlpconfig.Config client *http.Client stopCh chan struct{} } // NewClient creates a new HTTP metric client. func NewClient(opts ...Option) otlpmetric.Client { cfg := otlpconfig.NewDefaultConfig() otlpconfig.ApplyHTTPEnvConfigs(&cfg) for _, opt := range opts { opt.applyHTTPOption(&cfg) } for pathPtr, defaultPath := range map[*string]string{ &cfg.Metrics.URLPath: defaultMetricsPath, } { tmp := strings.TrimSpace(*pathPtr) if tmp == "" { tmp = defaultPath } else { tmp = path.Clean(tmp) if !path.IsAbs(tmp) { tmp = fmt.Sprintf("/%s", tmp) } } *pathPtr = tmp } if cfg.MaxAttempts <= 0 { cfg.MaxAttempts = defaultMaxAttempts } if cfg.MaxAttempts > defaultMaxAttempts { cfg.MaxAttempts = defaultMaxAttempts } if cfg.Backoff <= 0 { cfg.Backoff = defaultBackoff } httpClient := &http.Client{ Transport: ourTransport, Timeout: cfg.Metrics.Timeout, } if cfg.Metrics.TLSCfg != nil { transport := ourTransport.Clone() transport.TLSClientConfig = cfg.Metrics.TLSCfg httpClient.Transport = transport } stopCh := make(chan struct{}) return &client{ name: "metrics", cfg: cfg.Metrics, generalCfg: cfg, stopCh: stopCh, client: httpClient, } } // Start does nothing in a HTTP client func (d *client) Start(ctx context.Context) error { // nothing to do select { case <-ctx.Done(): return ctx.Err() default: } return nil } // Stop shuts down the client and interrupt any in-flight request. func (d *client) Stop(ctx context.Context) error { close(d.stopCh) select { case <-ctx.Done(): return ctx.Err() default: } return nil } // UploadMetrics sends a batch of metrics to the collector. func (d *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error { pbRequest := &colmetricpb.ExportMetricsServiceRequest{ ResourceMetrics: protoMetrics, } rawRequest, err := proto.Marshal(pbRequest) if err != nil { return err } return d.send(ctx, rawRequest) } func (d *client) send(ctx context.Context, rawRequest []byte) error { address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath) var cancel context.CancelFunc ctx, cancel = d.contextWithStop(ctx) defer cancel() for i := 0; i < d.generalCfg.MaxAttempts; i++ { response, err := d.singleSend(ctx, rawRequest, address) if err != nil { return err } // We don't care about the body, so try to read it // into /dev/null and close it immediately. The // reading part is to facilitate connection reuse. _, _ = io.Copy(ioutil.Discard, response.Body) _ = response.Body.Close() switch response.StatusCode { case http.StatusOK: return nil case http.StatusTooManyRequests: fallthrough case http.StatusServiceUnavailable: select { case <-time.After(getWaitDuration(d.generalCfg.Backoff, i)): continue case <-ctx.Done(): return ctx.Err() } default: return fmt.Errorf("failed to send %s to %s with HTTP status %s", d.name, address, response.Status) } } return fmt.Errorf("failed to send data to %s after %d tries", address, d.generalCfg.MaxAttempts) } func (d *client) getScheme() string { if d.cfg.Insecure { return "http" } return "https" } func getWaitDuration(backoff time.Duration, i int) time.Duration { // Strategy: after nth failed attempt, attempt resending after // k * initialBackoff + jitter, where k is a random number in // range [0, 2^n-1), and jitter is a random percentage of // initialBackoff from [-5%, 5%). // // Based on // https://en.wikipedia.org/wiki/Exponential_backoff#Example_exponential_backoff_algorithm // // Jitter is our addition. // There won't be an overflow, since i is capped to // defaultMaxAttempts (5). upperK := (int64)(1) << (i + 1) jitterPercent := (rand.Float64() - 0.5) / 10. jitter := jitterPercent * (float64)(backoff) k := rand.Int63n(upperK) return (time.Duration)(k)*backoff + (time.Duration)(jitter) } func (d *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { // Unify the parent context Done signal with the client's stop // channel. ctx, cancel := context.WithCancel(ctx) go func(ctx context.Context, cancel context.CancelFunc) { select { case <-ctx.Done(): // Nothing to do, either cancelled or deadline // happened. case <-d.stopCh: cancel() } }(ctx, cancel) return ctx, cancel } func (d *client) singleSend(ctx context.Context, rawRequest []byte, address string) (*http.Response, error) { request, err := http.NewRequestWithContext(ctx, http.MethodPost, address, nil) if err != nil { return nil, err } bodyReader, contentLength, headers := d.prepareBody(rawRequest) // Not closing bodyReader through defer, the HTTP Client's // Transport will do it for us request.Body = bodyReader request.ContentLength = contentLength for key, values := range headers { for _, value := range values { request.Header.Add(key, value) } } return d.client.Do(request) } func (d *client) prepareBody(rawRequest []byte) (io.ReadCloser, int64, http.Header) { var bodyReader io.ReadCloser headers := http.Header{} for k, v := range d.cfg.Headers { headers.Set(k, v) } contentLength := (int64)(len(rawRequest)) headers.Set("Content-Type", contentTypeProto) requestReader := bytes.NewBuffer(rawRequest) switch Compression(d.cfg.Compression) { case NoCompression: bodyReader = ioutil.NopCloser(requestReader) case GzipCompression: preader, pwriter := io.Pipe() go func() { defer pwriter.Close() gzipper := gzip.NewWriter(pwriter) defer gzipper.Close() _, err := io.Copy(gzipper, requestReader) if err != nil { otel.Handle(fmt.Errorf("otlphttp: failed to gzip request: %v", err)) } }() headers.Set("Content-Encoding", "gzip") bodyReader = preader contentLength = -1 } return bodyReader, contentLength, headers } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go000066400000000000000000000266631414226744000317550ustar00rootroot00000000000000// 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 otlpmetrichttp_test import ( "context" "fmt" "net/http" "os" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" "go.opentelemetry.io/otel/sdk/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" ) const ( relOtherMetricsPath = "post/metrics/here" otherMetricsPath = "/post/metrics/here" ) var ( oneRecord = otlpmetrictest.OneRecordReader() testResource = resource.Empty() ) var ( testHeaders = map[string]string{ "Otel-Go-Key-1": "somevalue", "Otel-Go-Key-2": "someothervalue", } ) func TestEndToEnd(t *testing.T) { tests := []struct { name string opts []otlpmetrichttp.Option mcCfg mockCollectorConfig tls bool }{ { name: "no extra options", opts: nil, }, { name: "with gzip compression", opts: []otlpmetrichttp.Option{ otlpmetrichttp.WithCompression(otlpmetrichttp.GzipCompression), }, }, { name: "with empty paths (forced to defaults)", opts: []otlpmetrichttp.Option{ otlpmetrichttp.WithURLPath(""), }, }, { name: "with relative paths", opts: []otlpmetrichttp.Option{ otlpmetrichttp.WithURLPath(relOtherMetricsPath), }, mcCfg: mockCollectorConfig{ MetricsURLPath: otherMetricsPath, }, }, { name: "with TLS", opts: nil, mcCfg: mockCollectorConfig{ WithTLS: true, }, tls: true, }, { name: "with extra headers", opts: []otlpmetrichttp.Option{ otlpmetrichttp.WithHeaders(testHeaders), }, mcCfg: mockCollectorConfig{ ExpectedHeaders: testHeaders, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mc := runMockCollector(t, tc.mcCfg) defer mc.MustStop(t) allOpts := []otlpmetrichttp.Option{ otlpmetrichttp.WithEndpoint(mc.Endpoint()), } if tc.tls { tlsConfig := mc.ClientTLSConfig() require.NotNil(t, tlsConfig) allOpts = append(allOpts, otlpmetrichttp.WithTLSClientConfig(tlsConfig)) } else { allOpts = append(allOpts, otlpmetrichttp.WithInsecure()) } allOpts = append(allOpts, tc.opts...) client := otlpmetrichttp.NewClient(allOpts...) ctx := context.Background() exporter, err := otlpmetric.New(ctx, client) if assert.NoError(t, err) { defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() otlpmetrictest.RunEndToEndTest(ctx, t, exporter, mc) } }) } } func TestExporterShutdown(t *testing.T) { mc := runMockCollector(t, mockCollectorConfig{}) defer func() { _ = mc.Stop() }() <-time.After(5 * time.Millisecond) otlpmetrictest.RunExporterShutdownTest(t, func() otlpmetric.Client { return otlpmetrichttp.NewClient( otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithEndpoint(mc.endpoint), ) }) } func TestRetry(t *testing.T) { statuses := []int{ http.StatusTooManyRequests, http.StatusServiceUnavailable, } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) client := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithMaxAttempts(len(statuses)+1), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, client) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.Export(ctx, testResource, oneRecord) assert.NoError(t, err) assert.Len(t, mc.GetMetrics(), 1) } func TestTimeout(t *testing.T) { mcCfg := mockCollectorConfig{ InjectDelay: 100 * time.Millisecond, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) client := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithTimeout(50*time.Millisecond), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, client) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.Export(ctx, testResource, oneRecord) assert.Equal(t, true, os.IsTimeout(err)) } func TestRetryFailed(t *testing.T) { statuses := []int{ http.StatusTooManyRequests, http.StatusServiceUnavailable, } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithMaxAttempts(1), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Empty(t, mc.GetMetrics()) } func TestNoRetry(t *testing.T) { statuses := []int{ http.StatusBadRequest, } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithMaxAttempts(len(statuses)+1), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Equal(t, fmt.Sprintf("failed to send metrics to http://%s/v1/metrics with HTTP status 400 Bad Request", mc.endpoint), err.Error()) assert.Empty(t, mc.GetMetrics()) } func TestEmptyData(t *testing.T) { mcCfg := mockCollectorConfig{} mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() assert.NoError(t, err) err = exporter.Export(ctx, testResource, oneRecord) assert.NoError(t, err) assert.NotEmpty(t, mc.GetMetrics()) } func TestUnreasonableMaxAttempts(t *testing.T) { // Max attempts is 5, we set collector to fail 7 times and try // to configure max attempts to be either negative or too // large. Since we set max attempts to 5 in such cases, // exporting to the collector should fail. type testcase struct { name string maxAttempts int } for _, tc := range []testcase{ { name: "negative max attempts", maxAttempts: -3, }, { name: "too large max attempts", maxAttempts: 10, }, } { t.Run(tc.name, func(t *testing.T) { statuses := make([]int, 0, 7) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithMaxAttempts(tc.maxAttempts), otlpmetrichttp.WithBackoff(time.Millisecond), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Empty(t, mc.GetMetrics()) }) } } func TestUnreasonableBackoff(t *testing.T) { // This sets backoff to negative value, which gets corrected // to default backoff instead of being used. Default max // attempts is 5, so we set the collector to fail 4 times, but // we set the deadline to 3 times of the default backoff, so // this should show that deadline is not met, meaning that the // retries weren't immediate (as negative backoff could // imply). statuses := make([]int, 0, 4) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithBackoff(-time.Millisecond), ) ctx, cancel := context.WithTimeout(context.Background(), 3*(300*time.Millisecond)) defer cancel() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(context.Background())) }() err = exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Empty(t, mc.GetMetrics()) } func TestCancelledContext(t *testing.T) { statuses := []int{ http.StatusBadRequest, } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), ) ctx, cancel := context.WithCancel(context.Background()) exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(context.Background())) }() cancel() _ = exporter.Export(ctx, testResource, oneRecord) assert.Empty(t, mc.GetMetrics()) } func TestDeadlineContext(t *testing.T) { statuses := make([]int, 0, 5) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithBackoff(time.Minute), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(context.Background())) }() ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() err = exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Empty(t, mc.GetMetrics()) } func TestStopWhileExporting(t *testing.T) { statuses := make([]int, 0, 5) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlpmetrichttp.NewClient( otlpmetrichttp.WithEndpoint(mc.Endpoint()), otlpmetrichttp.WithInsecure(), otlpmetrichttp.WithBackoff(time.Minute), ) ctx := context.Background() exporter, err := otlpmetric.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() doneCh := make(chan struct{}) go func() { err := exporter.Export(ctx, testResource, oneRecord) assert.Error(t, err) assert.Empty(t, mc.GetMetrics()) close(doneCh) }() <-time.After(time.Second) err = exporter.Shutdown(ctx) assert.NoError(t, err) <-doneCh } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go000066400000000000000000000020141414226744000301650ustar00rootroot00000000000000// 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 otlpmetrichttp provides a client that sends metrics to the collector using HTTP with binary protobuf payloads. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. */ package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/exporter.go000066400000000000000000000021531414226744000312740ustar00rootroot00000000000000// 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 otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" import ( "context" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" ) // New constructs a new Exporter and starts it. func New(ctx context.Context, opts ...Option) (*otlpmetric.Exporter, error) { return otlpmetric.New(ctx, NewClient(opts...)) } // NewUnstarted constructs a new Exporter and does not start it. func NewUnstarted(opts ...Option) *otlpmetric.Exporter { return otlpmetric.NewUnstarted(NewClient(opts...)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod000066400000000000000000000062511414226744000302060ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp go 1.15 require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/protobuf v1.27.1 ) replace go.opentelemetry.io/otel => ../../../.. replace go.opentelemetry.io/otel/sdk => ../../../../sdk replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric replace go.opentelemetry.io/otel/exporters/otlp => ../.. replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../ replace go.opentelemetry.io/otel/metric => ../../../../metric replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../../metric/prometheus replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../../trace/jaeger replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../../trace/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlpmetricgrpc replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/go.sum000066400000000000000000000315561414226744000302410ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/mock_collector_test.go000066400000000000000000000140711414226744000334640ustar00rootroot00000000000000// 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 otlpmetrichttp_test import ( "bytes" "compress/gzip" "context" "crypto/tls" "fmt" "io" "io/ioutil" "net" "net/http" "sync" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" ) type mockCollector struct { endpoint string server *http.Server spanLock sync.Mutex metricsStorage otlpmetrictest.MetricsStorage injectHTTPStatus []int injectContentType string injectDelay time.Duration clientTLSConfig *tls.Config expectedHeaders map[string]string } func (c *mockCollector) Stop() error { return c.server.Shutdown(context.Background()) } func (c *mockCollector) MustStop(t *testing.T) { assert.NoError(t, c.server.Shutdown(context.Background())) } func (c *mockCollector) GetMetrics() []*metricpb.Metric { c.spanLock.Lock() defer c.spanLock.Unlock() return c.metricsStorage.GetMetrics() } func (c *mockCollector) Endpoint() string { return c.endpoint } func (c *mockCollector) ClientTLSConfig() *tls.Config { return c.clientTLSConfig } func (c *mockCollector) serveMetrics(w http.ResponseWriter, r *http.Request) { if c.injectDelay != 0 { time.Sleep(c.injectDelay) } if !c.checkHeaders(r) { w.WriteHeader(http.StatusBadRequest) return } response := collectormetricpb.ExportMetricsServiceResponse{} rawResponse, err := proto.Marshal(&response) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } if injectedStatus := c.getInjectHTTPStatus(); injectedStatus != 0 { writeReply(w, rawResponse, injectedStatus, c.injectContentType) return } rawRequest, err := readRequest(r) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } request, err := unmarshalMetricsRequest(rawRequest, r.Header.Get("content-type")) if err != nil { w.WriteHeader(http.StatusBadRequest) return } writeReply(w, rawResponse, 0, c.injectContentType) c.spanLock.Lock() defer c.spanLock.Unlock() c.metricsStorage.AddMetrics(request) } func unmarshalMetricsRequest(rawRequest []byte, contentType string) (*collectormetricpb.ExportMetricsServiceRequest, error) { request := &collectormetricpb.ExportMetricsServiceRequest{} if contentType != "application/x-protobuf" { return request, fmt.Errorf("invalid content-type: %s, only application/x-protobuf is supported", contentType) } err := proto.Unmarshal(rawRequest, request) return request, err } func (c *mockCollector) checkHeaders(r *http.Request) bool { for k, v := range c.expectedHeaders { got := r.Header.Get(k) if got != v { return false } } return true } func (c *mockCollector) getInjectHTTPStatus() int { if len(c.injectHTTPStatus) == 0 { return 0 } status := c.injectHTTPStatus[0] c.injectHTTPStatus = c.injectHTTPStatus[1:] if len(c.injectHTTPStatus) == 0 { c.injectHTTPStatus = nil } return status } func readRequest(r *http.Request) ([]byte, error) { if r.Header.Get("Content-Encoding") == "gzip" { return readGzipBody(r.Body) } return ioutil.ReadAll(r.Body) } func readGzipBody(body io.Reader) ([]byte, error) { rawRequest := bytes.Buffer{} gunzipper, err := gzip.NewReader(body) if err != nil { return nil, err } defer gunzipper.Close() _, err = io.Copy(&rawRequest, gunzipper) if err != nil { return nil, err } return rawRequest.Bytes(), nil } func writeReply(w http.ResponseWriter, rawResponse []byte, injectHTTPStatus int, injectContentType string) { status := http.StatusOK if injectHTTPStatus != 0 { status = injectHTTPStatus } contentType := "application/x-protobuf" if injectContentType != "" { contentType = injectContentType } w.Header().Set("Content-Type", contentType) w.WriteHeader(status) _, _ = w.Write(rawResponse) } type mockCollectorConfig struct { MetricsURLPath string Port int InjectHTTPStatus []int InjectContentType string InjectDelay time.Duration WithTLS bool ExpectedHeaders map[string]string } func (c *mockCollectorConfig) fillInDefaults() { if c.MetricsURLPath == "" { c.MetricsURLPath = otlpconfig.DefaultMetricsPath } } func runMockCollector(t *testing.T, cfg mockCollectorConfig) *mockCollector { cfg.fillInDefaults() ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", cfg.Port)) require.NoError(t, err) _, portStr, err := net.SplitHostPort(ln.Addr().String()) require.NoError(t, err) m := &mockCollector{ endpoint: fmt.Sprintf("localhost:%s", portStr), metricsStorage: otlpmetrictest.NewMetricsStorage(), injectHTTPStatus: cfg.InjectHTTPStatus, injectContentType: cfg.InjectContentType, injectDelay: cfg.InjectDelay, expectedHeaders: cfg.ExpectedHeaders, } mux := http.NewServeMux() mux.Handle(cfg.MetricsURLPath, http.HandlerFunc(m.serveMetrics)) server := &http.Server{ Handler: mux, } if cfg.WithTLS { pem, err := generateWeakCertificate() require.NoError(t, err) tlsCertificate, err := tls.X509KeyPair(pem.Certificate, pem.PrivateKey) require.NoError(t, err) server.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{tlsCertificate}, } m.clientTLSConfig = &tls.Config{ InsecureSkipVerify: true, } } go func() { if cfg.WithTLS { _ = server.ServeTLS(ln, "", "") } else { _ = server.Serve(ln) } }() m.server = server return m } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlpmetric/otlpmetrichttp/options.go000066400000000000000000000104501414226744000311160ustar00rootroot00000000000000// 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 otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" import ( "crypto/tls" "time" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" ) const ( // defaultMaxAttempts describes how many times the driver // should retry the sending of the payload in case of a // retryable error. defaultMaxAttempts int = 5 // defaultMetricsPath is a default URL path for endpoint that // receives metrics. defaultMetricsPath string = "/v1/metrics" // defaultBackoff is a default base backoff time used in the // exponential backoff strategy. defaultBackoff time.Duration = 300 * time.Millisecond ) // Compression describes the compression used for payloads sent to the // collector. type Compression otlpconfig.Compression const ( // NoCompression tells the driver to send payloads without // compression. NoCompression = Compression(otlpconfig.NoCompression) // GzipCompression tells the driver to send payloads after // compressing them with gzip. GzipCompression = Compression(otlpconfig.GzipCompression) ) // Option applies an option to the HTTP client. type Option interface { applyHTTPOption(*otlpconfig.Config) } type wrappedOption struct { otlpconfig.HTTPOption } func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) { w.ApplyHTTPOption(cfg) } // WithEndpoint allows one to set the address of the collector // endpoint that the driver will use to send metrics. If // unset, it will instead try to use // the default endpoint (localhost:4317). Note that the endpoint // must not contain any URL path. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } // WithCompression tells the driver to compress the sent data. func WithCompression(compression Compression) Option { return wrappedOption{otlpconfig.WithCompression(otlpconfig.Compression(compression))} } // WithURLPath allows one to override the default URL path used // for sending metrics. If unset, default ("/v1/metrics") will be used. func WithURLPath(urlPath string) Option { return wrappedOption{otlpconfig.WithURLPath(urlPath)} } // WithMaxAttempts allows one to override how many times the driver // will try to send the payload in case of retryable errors. // The max attempts is limited to at most 5 retries. If unset, // default (5) will be used. func WithMaxAttempts(maxAttempts int) Option { return wrappedOption{otlpconfig.WithMaxAttempts(maxAttempts)} } // WithBackoff tells the driver to use the duration as a base of the // exponential backoff strategy. If unset, default (300ms) will be // used. func WithBackoff(duration time.Duration) Option { return wrappedOption{otlpconfig.WithBackoff(duration)} } // WithTLSClientConfig can be used to set up a custom TLS // configuration for the client used to send payloads to the // collector. Use it if you want to use a custom certificate. func WithTLSClientConfig(tlsCfg *tls.Config) Option { return wrappedOption{otlpconfig.WithTLSClientConfig(tlsCfg)} } // WithInsecure tells the driver to connect to the collector using the // HTTP scheme, instead of HTTPS. func WithInsecure() Option { return wrappedOption{otlpconfig.WithInsecure()} } // WithHeaders allows one to tell the driver to send additional HTTP // headers with the payloads. Specifying headers like Content-Length, // Content-Encoding and Content-Type may result in a broken driver. func WithHeaders(headers map[string]string) Option { return wrappedOption{otlpconfig.WithHeaders(headers)} } // WithTimeout tells the driver the max waiting time for the backend to process // each metrics batch. If unset, the default will be 10 seconds. func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/000077500000000000000000000000001414226744000236255ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/README.md000066400000000000000000000053761414226744000251170ustar00rootroot00000000000000# OpenTelemetry-Go OTLP Span Exporter [![Go Reference](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/otlp/otlptrace.svg)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace) [OpenTelemetry Protocol Exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.5.0/specification/protocol/exporter.md) implementation. ## Installation ``` go get -u go.opentelemetry.io/otel/exporters/otlp/otlptrace ``` ## Examples - [Exporter setup and examples](./otlptracehttp/example_test.go) - [Full example sending telemetry to a local collector](../../../example/otel-collector) ## [`otlptrace`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace) The `otlptrace` package provides an exporter implementing the OTel span exporter interface. This exporter is configured using a client satisfying the `otlptrace.Client` interface. This client handles the transformation of data into wire format and the transmission of that data to the collector. ## [`otlptracegrpc`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc) The `otlptracegrpc` package implements a client for the span exporter that sends trace telemetry data to the collector using gRPC with protobuf-encoded payloads. ## [`otlptracehttp`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp) The `otlptracehttp` package implements a client for the span exporter that sends trace telemetry data to the collector using HTTP with protobuf-encoded payloads. ## Configuration ### Environment Variables The following environment variables can be used (instead of options objects) to override the default configuration. | Environment variable | Option | Default value | | ------------------------------------------------------------------------ |------------------------------ | ----------------------------------- | | `OTEL_EXPORTER_OTLP_ENDPOINT` `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `WithEndpoint` `WithInsecure` | `https://localhost:4317` | | `OTEL_EXPORTER_OTLP_CERTIFICATE` `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` | `WithTLSClientConfig` | | | `OTEL_EXPORTER_OTLP_HEADERS` `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `WithHeaders` | | | `OTEL_EXPORTER_OTLP_COMPRESSION` `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | `WithCompression` | | | `OTEL_EXPORTER_OTLP_TIMEOUT` `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `WithTimeout` | `10s` | Configuration using options have precedence over the environment variables. golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/clients.go000066400000000000000000000044151414226744000256210ustar00rootroot00000000000000// 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" import ( "context" tracepb "go.opentelemetry.io/proto/otlp/trace/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 { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // 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 // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // 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 UploadTraces, 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 // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // UploadTraces should transform the passed traces to the wire // format and send it to the collector. May be called // concurrently. UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/exporter.go000066400000000000000000000045411414226744000260300ustar00rootroot00000000000000// 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" import ( "context" "errors" "sync" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" tracesdk "go.opentelemetry.io/otel/sdk/trace" ) var ( errAlreadyStarted = errors.New("already started") ) // Exporter exports trace data in the OTLP wire format. type Exporter struct { client Client mu sync.RWMutex started bool startOnce sync.Once stopOnce sync.Once } // ExportSpans exports a batch of spans. func (e *Exporter) ExportSpans(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { protoSpans := tracetransform.Spans(ss) if len(protoSpans) == 0 { return nil } return e.client.UploadTraces(ctx, protoSpans) } // 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 } // Shutdown flushes all exports and closes all connections to the receiving endpoint. func (e *Exporter) Shutdown(ctx context.Context) error { e.mu.RLock() started := e.started e.mu.RUnlock() if !started { return nil } var err error e.stopOnce.Do(func() { err = e.client.Stop(ctx) e.mu.Lock() e.started = false e.mu.Unlock() }) return err } var _ tracesdk.SpanExporter = (*Exporter)(nil) // New constructs a new Exporter and starts it. func New(ctx context.Context, client Client) (*Exporter, error) { exp := NewUnstarted(client) 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) *Exporter { return &Exporter{ client: client, } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/go.mod000066400000000000000000000056511414226744000247420ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlptrace go 1.15 require ( github.com/cenkalti/backoff/v4 v4.1.1 github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.41.0 google.golang.org/protobuf v1.27.1 ) replace go.opentelemetry.io/otel => ../../.. replace go.opentelemetry.io/otel/sdk => ../../../sdk replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../prometheus replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ./ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ./otlptracegrpc replace go.opentelemetry.io/otel/exporters/jaeger => ../../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ./otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/go.sum000066400000000000000000000314251414226744000247650ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/000077500000000000000000000000001414226744000254415ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/connection/000077500000000000000000000000001414226744000276005ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/connection/alignment_test.go000066400000000000000000000020031414226744000331370ustar00rootroot00000000000000// 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 connection import ( "os" "testing" "unsafe" ottest "go.opentelemetry.io/otel/internal/internaltest" ) // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []ottest.FieldOffset{ { Name: "Connection.lastConnectErrPtr", Offset: unsafe.Offsetof(Connection{}.lastConnectErrPtr), }, } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/connection/connection.go000066400000000000000000000213651414226744000322750ustar00rootroot00000000000000// 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 connection import ( "context" "math/rand" "sync" "sync/atomic" "time" "unsafe" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/grpc/encoding/gzip" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) type Connection struct { // Ensure pointer is 64-bit aligned for atomic operations on both 32 and 64 bit machines. lastConnectErrPtr unsafe.Pointer // mu protects the Connection as it is accessed by the // exporter goroutines and background Connection goroutine mu sync.Mutex cc *grpc.ClientConn // these fields are read-only after constructor is finished cfg otlpconfig.Config SCfg otlpconfig.SignalConfig requestFunc retry.RequestFunc metadata metadata.MD newConnectionHandler func(cc *grpc.ClientConn) // these channels are created once disconnectedCh chan bool backgroundConnectionDoneCh chan struct{} stopCh chan struct{} // this is for tests, so they can replace the closing // routine without a worry of modifying some global variable // or changing it back to original after the test is done closeBackgroundConnectionDoneCh func(ch chan struct{}) } func NewConnection(cfg otlpconfig.Config, sCfg otlpconfig.SignalConfig, handler func(cc *grpc.ClientConn)) *Connection { c := new(Connection) c.newConnectionHandler = handler c.cfg = cfg c.requestFunc = cfg.RetryConfig.RequestFunc(evaluate) c.SCfg = sCfg if len(c.SCfg.Headers) > 0 { c.metadata = metadata.New(c.SCfg.Headers) } c.closeBackgroundConnectionDoneCh = func(ch chan struct{}) { close(ch) } return c } func (c *Connection) StartConnection(ctx context.Context) error { c.stopCh = make(chan struct{}) c.disconnectedCh = make(chan bool, 1) c.backgroundConnectionDoneCh = make(chan struct{}) if err := c.connect(ctx); err == nil { c.setStateConnected() } else { c.SetStateDisconnected(err) } go c.indefiniteBackgroundConnection() // TODO: proper error handling when initializing connections. // We can report permanent errors, e.g., invalid settings. return nil } func (c *Connection) LastConnectError() error { errPtr := (*error)(atomic.LoadPointer(&c.lastConnectErrPtr)) if errPtr == nil { return nil } return *errPtr } func (c *Connection) saveLastConnectError(err error) { var errPtr *error if err != nil { errPtr = &err } atomic.StorePointer(&c.lastConnectErrPtr, unsafe.Pointer(errPtr)) } func (c *Connection) SetStateDisconnected(err error) { c.saveLastConnectError(err) select { case c.disconnectedCh <- true: default: } c.newConnectionHandler(nil) } func (c *Connection) setStateConnected() { c.saveLastConnectError(nil) } func (c *Connection) Connected() bool { return c.LastConnectError() == nil } const defaultConnReattemptPeriod = 10 * time.Second func (c *Connection) indefiniteBackgroundConnection() { defer func() { c.closeBackgroundConnectionDoneCh(c.backgroundConnectionDoneCh) }() connReattemptPeriod := c.cfg.ReconnectionPeriod if connReattemptPeriod <= 0 { connReattemptPeriod = defaultConnReattemptPeriod } // No strong seeding required, nano time can // already help with pseudo uniqueness. rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024))) // maxJitterNanos: 70% of the connectionReattemptPeriod maxJitterNanos := int64(0.7 * float64(connReattemptPeriod)) for { // Otherwise these will be the normal scenarios to enable // reconnection if we trip out. // 1. If we've stopped, return entirely // 2. Otherwise block until we are disconnected, and // then retry connecting select { case <-c.stopCh: return case <-c.disconnectedCh: // Quickly check if we haven't stopped at the // same time. select { case <-c.stopCh: return default: } // Normal scenario that we'll wait for } if err := c.connect(context.Background()); err == nil { c.setStateConnected() } else { // this code is unreachable in most cases // c.connect does not establish Connection c.SetStateDisconnected(err) } // Apply some jitter to avoid lockstep retrials of other // collector-exporters. Lockstep retrials could result in an // innocent DDOS, by clogging the machine's resources and network. jitter := time.Duration(rng.Int63n(maxJitterNanos)) select { case <-c.stopCh: return case <-time.After(connReattemptPeriod + jitter): } } } func (c *Connection) connect(ctx context.Context) error { cc, err := c.dialToCollector(ctx) if err != nil { return err } c.setConnection(cc) c.newConnectionHandler(cc) return nil } // setConnection sets cc as the client Connection and returns true if // the Connection state changed. func (c *Connection) setConnection(cc *grpc.ClientConn) bool { c.mu.Lock() defer c.mu.Unlock() // If previous clientConn is same as the current then just return. // This doesn't happen right now as this func is only called with new ClientConn. // It is more about future-proofing. if c.cc == cc { return false } // If the previous clientConn was non-nil, close it if c.cc != nil { _ = c.cc.Close() } c.cc = cc return true } func (c *Connection) dialToCollector(ctx context.Context) (*grpc.ClientConn, error) { if c.cfg.GRPCConn != nil { return c.cfg.GRPCConn, nil } dialOpts := []grpc.DialOption{} if c.cfg.ServiceConfig != "" { dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(c.cfg.ServiceConfig)) } if c.SCfg.GRPCCredentials != nil { dialOpts = append(dialOpts, grpc.WithTransportCredentials(c.SCfg.GRPCCredentials)) } else if c.SCfg.Insecure { dialOpts = append(dialOpts, grpc.WithInsecure()) } if c.SCfg.Compression == otlpconfig.GzipCompression { dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name))) } if len(c.cfg.DialOptions) != 0 { dialOpts = append(dialOpts, c.cfg.DialOptions...) } ctx, cancel := c.ContextWithStop(ctx) defer cancel() ctx = c.ContextWithMetadata(ctx) return grpc.DialContext(ctx, c.SCfg.Endpoint, dialOpts...) } func (c *Connection) ContextWithMetadata(ctx context.Context) context.Context { if c.metadata.Len() > 0 { return metadata.NewOutgoingContext(ctx, c.metadata) } return ctx } func (c *Connection) Shutdown(ctx context.Context) error { close(c.stopCh) // Ensure that the backgroundConnector returns select { case <-c.backgroundConnectionDoneCh: case <-ctx.Done(): return ctx.Err() } c.mu.Lock() cc := c.cc c.cc = nil c.mu.Unlock() if cc != nil { return cc.Close() } return nil } func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { // Unify the parent context Done signal with the Connection's // stop channel. ctx, cancel := context.WithCancel(ctx) go func(ctx context.Context, cancel context.CancelFunc) { select { case <-ctx.Done(): // Nothing to do, either cancelled or deadline // happened. case <-c.stopCh: cancel() } }(ctx, cancel) return ctx, cancel } func (c *Connection) DoRequest(ctx context.Context, fn func(context.Context) error) error { ctx, cancel := c.ContextWithStop(ctx) defer cancel() return c.requestFunc(ctx, func(ctx context.Context) error { err := fn(ctx) // nil is converted to OK. if status.Code(err) == codes.OK { // Success. return nil } return err }) } // evaluate returns if err is retry-able and a duration to wait for if an // explicit throttle time is included in err. func evaluate(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(status *status.Status) time.Duration { for _, detail := range status.Details() { if t, ok := detail.(*errdetails.RetryInfo); ok { return t.RetryDelay.AsDuration() } } return 0 } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/connection/connection_test.go000066400000000000000000000075261414226744000333370ustar00rootroot00000000000000// 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 connection import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" ) func TestThrottleDuration(t *testing.T) { c := codes.ResourceExhausted testcases := []struct { status *status.Status expected time.Duration }{ { status: status.New(c, "no retry info"), expected: 0, }, { status: func() *status.Status { s, err := status.New(c, "single retry info").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, "error info").WithDetails( &errdetails.ErrorInfo{Reason: "no throttle detail"}, ) require.NoError(t, err) return s }(), expected: 0, }, { status: func() *status.Status { s, err := status.New(c, "error and retry info").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, "double retry info").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 TestEvaluate(t *testing.T) { retryable := 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 retryable { got, _ := evaluate(status.Error(c, "")) assert.Equalf(t, want, got, "evaluate(%s)", c) } } func TestDoRequest(t *testing.T) { ev := func(error) (bool, time.Duration) { return false, 0 } c := new(Connection) c.requestFunc = retry.Config{}.RequestFunc(ev) c.stopCh = make(chan struct{}) ctx := context.Background() assert.NoError(t, c.DoRequest(ctx, func(ctx context.Context) error { return nil })) assert.NoError(t, c.DoRequest(ctx, func(ctx context.Context) error { return status.Error(codes.OK, "") })) assert.ErrorIs(t, c.DoRequest(ctx, func(ctx context.Context) error { return assert.AnError }), assert.AnError) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/000077500000000000000000000000001414226744000276055ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/envconfig.go000066400000000000000000000116111414226744000321120ustar00rootroot00000000000000// 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 otlpconfig import ( "crypto/tls" "fmt" "io/ioutil" "net/url" "os" "regexp" "strconv" "strings" "time" "go.opentelemetry.io/otel" ) var httpSchemeRegexp = regexp.MustCompile(`(?i)^(http://|https://)`) func ApplyGRPCEnvConfigs(cfg *Config) { e := EnvOptionsReader{ GetEnv: os.Getenv, ReadFile: ioutil.ReadFile, } e.ApplyGRPCEnvConfigs(cfg) } func ApplyHTTPEnvConfigs(cfg *Config) { e := EnvOptionsReader{ GetEnv: os.Getenv, ReadFile: ioutil.ReadFile, } e.ApplyHTTPEnvConfigs(cfg) } type EnvOptionsReader struct { GetEnv func(string) string ReadFile func(filename string) ([]byte, error) } func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) { opts := e.GetOptionsFromEnv() for _, opt := range opts { opt.ApplyHTTPOption(cfg) } } func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg *Config) { opts := e.GetOptionsFromEnv() for _, opt := range opts { opt.ApplyGRPCOption(cfg) } } func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption { var opts []GenericOption // Endpoint if v, ok := e.getEnvValue("ENDPOINT"); ok { if isInsecureEndpoint(v) { opts = append(opts, WithInsecure()) } else { opts = append(opts, WithSecure()) } opts = append(opts, WithEndpoint(trimSchema(v))) } if v, ok := e.getEnvValue("TRACES_ENDPOINT"); ok { if isInsecureEndpoint(v) { opts = append(opts, WithInsecure()) } else { opts = append(opts, WithSecure()) } opts = append(opts, WithEndpoint(trimSchema(v))) } // Certificate File if path, ok := e.getEnvValue("CERTIFICATE"); ok { if tls, err := e.readTLSConfig(path); err == nil { opts = append(opts, WithTLSClientConfig(tls)) } else { otel.Handle(fmt.Errorf("failed to configure otlp exporter certificate '%s': %w", path, err)) } } if path, ok := e.getEnvValue("TRACES_CERTIFICATE"); ok { if tls, err := e.readTLSConfig(path); err == nil { opts = append(opts, WithTLSClientConfig(tls)) } else { otel.Handle(fmt.Errorf("failed to configure otlp traces exporter certificate '%s': %w", path, err)) } } // Headers if h, ok := e.getEnvValue("HEADERS"); ok { opts = append(opts, WithHeaders(stringToHeader(h))) } if h, ok := e.getEnvValue("TRACES_HEADERS"); ok { opts = append(opts, WithHeaders(stringToHeader(h))) } // Compression if c, ok := e.getEnvValue("COMPRESSION"); ok { opts = append(opts, WithCompression(stringToCompression(c))) } if c, ok := e.getEnvValue("TRACES_COMPRESSION"); ok { opts = append(opts, WithCompression(stringToCompression(c))) } // Timeout if t, ok := e.getEnvValue("TIMEOUT"); ok { if d, err := strconv.Atoi(t); err == nil { opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond)) } } if t, ok := e.getEnvValue("TRACES_TIMEOUT"); ok { if d, err := strconv.Atoi(t); err == nil { opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond)) } } return opts } func isInsecureEndpoint(endpoint string) bool { return strings.HasPrefix(strings.ToLower(endpoint), "http://") || strings.HasPrefix(strings.ToLower(endpoint), "unix://") } func trimSchema(endpoint string) string { return httpSchemeRegexp.ReplaceAllString(endpoint, "") } // getEnvValue gets an OTLP environment variable value of the specified key using the GetEnv function. // This function already prepends the OTLP prefix to all key lookup. func (e *EnvOptionsReader) getEnvValue(key string) (string, bool) { v := strings.TrimSpace(e.GetEnv(fmt.Sprintf("OTEL_EXPORTER_OTLP_%s", key))) return v, v != "" } func (e *EnvOptionsReader) readTLSConfig(path string) (*tls.Config, error) { b, err := e.ReadFile(path) if err != nil { return nil, err } return CreateTLSConfig(b) } func stringToCompression(value string) Compression { switch value { case "gzip": return GzipCompression } return NoCompression } func stringToHeader(value string) map[string]string { headersPairs := strings.Split(value, ",") headers := make(map[string]string) for _, header := range headersPairs { nameValue := strings.SplitN(header, "=", 2) if len(nameValue) < 2 { continue } name, err := url.QueryUnescape(nameValue[0]) if err != nil { continue } trimmedName := strings.TrimSpace(name) value, err := url.QueryUnescape(nameValue[1]) if err != nil { continue } trimmedValue := strings.TrimSpace(value) headers[trimmedName] = trimmedValue } return headers } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/envconfig_test.go000066400000000000000000000034351414226744000331560ustar00rootroot00000000000000// 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 otlpconfig import ( "reflect" "testing" ) func TestStringToHeader(t *testing.T) { tests := []struct { name string value string want map[string]string }{ { name: "simple test", value: "userId=alice", want: map[string]string{"userId": "alice"}, }, { name: "simple test with spaces", value: " userId = alice ", want: map[string]string{"userId": "alice"}, }, { name: "multiples headers encoded", value: "userId=alice,serverNode=DF%3A28,isProduction=false", want: map[string]string{ "userId": "alice", "serverNode": "DF:28", "isProduction": "false", }, }, { name: "invalid headers format", value: "userId:alice", want: map[string]string{}, }, { name: "invalid key", value: "%XX=missing,userId=alice", want: map[string]string{ "userId": "alice", }, }, { name: "invalid value", value: "missing=%XX,userId=alice", want: map[string]string{ "userId": "alice", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := stringToHeader(tt.value); !reflect.DeepEqual(got, tt.want) { t.Errorf("stringToHeader() = %v, want %v", got, tt.want) } }) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/options.go000066400000000000000000000127731414226744000316410ustar00rootroot00000000000000// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" import ( "crypto/tls" "fmt" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" ) const ( // DefaultTracesPath is a default URL path for endpoint that // receives spans. DefaultTracesPath string = "/v1/traces" // DefaultTimeout is a default max waiting time for the backend to process // each span batch. DefaultTimeout time.Duration = 10 * time.Second ) type ( SignalConfig struct { Endpoint string Insecure bool TLSCfg *tls.Config Headers map[string]string Compression Compression Timeout time.Duration URLPath string // gRPC configurations GRPCCredentials credentials.TransportCredentials } Config struct { // Signal specific configurations Traces SignalConfig RetryConfig retry.Config // gRPC configurations ReconnectionPeriod time.Duration ServiceConfig string DialOptions []grpc.DialOption GRPCConn *grpc.ClientConn } ) func NewDefaultConfig() Config { c := Config{ Traces: SignalConfig{ Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorPort), URLPath: DefaultTracesPath, Compression: NoCompression, Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, } return c } type ( // GenericOption applies an option to the HTTP or gRPC driver. GenericOption interface { ApplyHTTPOption(*Config) ApplyGRPCOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } // HTTPOption applies an option to the HTTP driver. HTTPOption interface { ApplyHTTPOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } // GRPCOption applies an option to the gRPC driver. GRPCOption interface { ApplyGRPCOption(*Config) // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } ) // genericOption is an option that applies the same logic // for both gRPC and HTTP. type genericOption struct { fn func(*Config) } func (g *genericOption) ApplyGRPCOption(cfg *Config) { g.fn(cfg) } func (g *genericOption) ApplyHTTPOption(cfg *Config) { g.fn(cfg) } func (genericOption) private() {} func newGenericOption(fn func(cfg *Config)) GenericOption { return &genericOption{fn: fn} } // splitOption is an option that applies different logics // for gRPC and HTTP. type splitOption struct { httpFn func(*Config) grpcFn func(*Config) } func (g *splitOption) ApplyGRPCOption(cfg *Config) { g.grpcFn(cfg) } func (g *splitOption) ApplyHTTPOption(cfg *Config) { g.httpFn(cfg) } func (splitOption) private() {} func newSplitOption(httpFn func(cfg *Config), grpcFn func(cfg *Config)) GenericOption { return &splitOption{httpFn: httpFn, grpcFn: grpcFn} } // httpOption is an option that is only applied to the HTTP driver. type httpOption struct { fn func(*Config) } func (h *httpOption) ApplyHTTPOption(cfg *Config) { h.fn(cfg) } func (httpOption) private() {} func NewHTTPOption(fn func(cfg *Config)) HTTPOption { return &httpOption{fn: fn} } // grpcOption is an option that is only applied to the gRPC driver. type grpcOption struct { fn func(*Config) } func (h *grpcOption) ApplyGRPCOption(cfg *Config) { h.fn(cfg) } func (grpcOption) private() {} func NewGRPCOption(fn func(cfg *Config)) GRPCOption { return &grpcOption{fn: fn} } // Generic Options func WithEndpoint(endpoint string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Endpoint = endpoint }) } func WithCompression(compression Compression) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Compression = compression }) } func WithURLPath(urlPath string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.URLPath = urlPath }) } func WithRetry(rc retry.Config) GenericOption { return newGenericOption(func(cfg *Config) { cfg.RetryConfig = rc }) } func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption { return newSplitOption(func(cfg *Config) { cfg.Traces.TLSCfg = tlsCfg.Clone() }, func(cfg *Config) { cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg) }) } func WithInsecure() GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Insecure = true }) } func WithSecure() GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Insecure = false }) } func WithHeaders(headers map[string]string) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Headers = headers }) } func WithTimeout(duration time.Duration) GenericOption { return newGenericOption(func(cfg *Config) { cfg.Traces.Timeout = duration }) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/options_test.go000066400000000000000000000310551414226744000326720ustar00rootroot00000000000000// 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 otlpconfig_test import ( "errors" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "github.com/stretchr/testify/assert" ) const ( WeakCertificate = ` -----BEGIN CERTIFICATE----- MIIBhzCCASygAwIBAgIRANHpHgAWeTnLZpTSxCKs0ggwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEChMHb3RlbC1nbzAeFw0yMTA0MDExMzU5MDNaFw0yMTA0MDExNDU5MDNa MBIxEDAOBgNVBAoTB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS9 nWSkmPCxShxnp43F+PrOtbGV7sNfkbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0Z sJCLHGogQsYnWJBXUZOVo2MwYTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAsBgNVHREEJTAjgglsb2NhbGhvc3SHEAAA AAAAAAAAAAAAAAAAAAGHBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhANwZVVKvfvQ/ 1HXsTvgH+xTQswOwSSKYJ1cVHQhqK7ZbAiEAus8NxpTRnp5DiTMuyVmhVNPB+bVH Lhnm4N/QDk5rek0= -----END CERTIFICATE----- ` WeakPrivateKey = ` -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgN8HEXiXhvByrJ1zK SFT6Y2l2KqDWwWzKf+t4CyWrNKehRANCAAS9nWSkmPCxShxnp43F+PrOtbGV7sNf kbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0ZsJCLHGogQsYnWJBXUZOV -----END PRIVATE KEY----- ` ) type env map[string]string func (e *env) getEnv(env string) string { return (*e)[env] } type fileReader map[string][]byte func (f *fileReader) readFile(filename string) ([]byte, error) { if b, ok := (*f)[filename]; ok { return b, nil } return nil, errors.New("File not found") } func TestConfigs(t *testing.T) { tlsCert, err := otlpconfig.CreateTLSConfig([]byte(WeakCertificate)) assert.NoError(t, err) tests := []struct { name string opts []otlpconfig.GenericOption env env fileReader fileReader asserts func(t *testing.T, c *otlpconfig.Config, grpcOption bool) }{ { name: "Test default configs", asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "localhost:4317", c.Traces.Endpoint) assert.Equal(t, otlpconfig.NoCompression, c.Traces.Compression) assert.Equal(t, map[string]string(nil), c.Traces.Headers) assert.Equal(t, 10*time.Second, c.Traces.Timeout) }, }, // Endpoint Tests { name: "Test With Endpoint", opts: []otlpconfig.GenericOption{ otlpconfig.WithEndpoint("someendpoint"), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "someendpoint", c.Traces.Endpoint) }, }, { name: "Test Environment Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Traces.Endpoint) }, }, { name: "Test Environment Signal Specific Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "env_traces_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) }, }, { name: "Test Mixed Environment and With Endpoint", opts: []otlpconfig.GenericOption{ otlpconfig.WithEndpoint("traces_endpoint"), }, env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "traces_endpoint", c.Traces.Endpoint) }, }, { name: "Test Environment Endpoint with HTTP scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Traces.Endpoint) assert.Equal(t, true, c.Traces.Insecure) }, }, { name: "Test Environment Endpoint with HTTP scheme and leading & trailingspaces", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": " http://env_endpoint ", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Traces.Endpoint) assert.Equal(t, true, c.Traces.Insecure) }, }, { name: "Test Environment Endpoint with HTTPS scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "https://env_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_endpoint", c.Traces.Endpoint) assert.Equal(t, false, c.Traces.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://env_traces_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) assert.Equal(t, true, c.Traces.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint #2", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://env_traces_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) assert.Equal(t, true, c.Traces.Insecure) }, }, { name: "Test Environment Signal Specific Endpoint with uppercase scheme", env: map[string]string{ "OTEL_EXPORTER_OTLP_ENDPOINT": "HTTP://overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "HtTp://env_traces_endpoint", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) assert.Equal(t, true, c.Traces.Insecure) }, }, // Certificate tests { name: "Test With Certificate", opts: []otlpconfig.GenericOption{ otlpconfig.WithTLSClientConfig(tlsCert), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { if grpcOption { //TODO: make sure gRPC's credentials actually works assert.NotNil(t, c.Traces.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Environment Certificate", 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) { if grpcOption { assert.NotNil(t, c.Traces.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Environment Signal Specific Certificate", env: map[string]string{ "OTEL_EXPORTER_OTLP_CERTIFICATE": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE": "cert_path", }, fileReader: fileReader{ "cert_path": []byte(WeakCertificate), "invalid_cert": []byte("invalid certificate file."), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { if grpcOption { assert.NotNil(t, c.Traces.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) } }, }, { name: "Test Mixed Environment and With Certificate", opts: []otlpconfig.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) { if grpcOption { assert.NotNil(t, c.Traces.GRPCCredentials) } else { assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) } }, }, // Headers tests { name: "Test With Headers", opts: []otlpconfig.GenericOption{ otlpconfig.WithHeaders(map[string]string{"h1": "v1"}), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1"}, c.Traces.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) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) }, }, { name: "Test Environment Signal Specific Headers", env: map[string]string{ "OTEL_EXPORTER_OTLP_HEADERS": "overrode_by_signal_specific", "OTEL_EXPORTER_OTLP_TRACES_HEADERS": "h1=v1,h2=v2", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) }, }, { name: "Test Mixed Environment and With Headers", env: map[string]string{"OTEL_EXPORTER_OTLP_HEADERS": "h1=v1,h2=v2"}, opts: []otlpconfig.GenericOption{}, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) }, }, // Compression Tests { name: "Test With Compression", opts: []otlpconfig.GenericOption{ otlpconfig.WithCompression(otlpconfig.GzipCompression), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, otlpconfig.GzipCompression, c.Traces.Compression) }, }, { name: "Test Environment Compression", 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.Traces.Compression) }, }, { name: "Test Environment Signal Specific Compression", env: map[string]string{ "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION": "gzip", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, otlpconfig.GzipCompression, c.Traces.Compression) }, }, { name: "Test Mixed Environment and With Compression", opts: []otlpconfig.GenericOption{ otlpconfig.WithCompression(otlpconfig.NoCompression), }, env: map[string]string{ "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION": "gzip", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, otlpconfig.NoCompression, c.Traces.Compression) }, }, // Timeout Tests { name: "Test With Timeout", opts: []otlpconfig.GenericOption{ otlpconfig.WithTimeout(time.Duration(5 * time.Second)), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, 5*time.Second, c.Traces.Timeout) }, }, { name: "Test Environment Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Traces.Timeout, 15*time.Second) }, }, { name: "Test Environment Signal Specific Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT": "27000", }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Traces.Timeout, 27*time.Second) }, }, { name: "Test Mixed Environment and With Timeout", env: map[string]string{ "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT": "27000", }, opts: []otlpconfig.GenericOption{ otlpconfig.WithTimeout(5 * time.Second), }, asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { assert.Equal(t, c.Traces.Timeout, 5*time.Second) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { e := otlpconfig.EnvOptionsReader{ GetEnv: tt.env.getEnv, ReadFile: tt.fileReader.readFile, } // Tests Generic options as HTTP Options cfg := otlpconfig.NewDefaultConfig() e.ApplyHTTPEnvConfigs(&cfg) for _, opt := range tt.opts { opt.ApplyHTTPOption(&cfg) } tt.asserts(t, &cfg, false) // Tests Generic options as gRPC Options cfg = otlpconfig.NewDefaultConfig() e.ApplyGRPCEnvConfigs(&cfg) for _, opt := range tt.opts { opt.ApplyGRPCOption(&cfg) } tt.asserts(t, &cfg, true) }) } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/optiontypes.go000066400000000000000000000031551414226744000325350ustar00rootroot00000000000000// 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 otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" const ( // DefaultCollectorPort is the port the Exporter will attempt connect to // if no collector port is provided. DefaultCollectorPort uint16 = 4317 // DefaultCollectorHost is the host address the Exporter will attempt // connect to if no collector address is provided. DefaultCollectorHost string = "localhost" ) // Compression describes the compression used for payloads sent to the // collector. type Compression int const ( // NoCompression tells the driver to send payloads without // compression. NoCompression Compression = iota // GzipCompression tells the driver to send payloads after // compressing them with gzip. GzipCompression ) // Marshaler describes the kind of message format sent to the collector type Marshaler int const ( // MarshalProto tells the driver to send using the protobuf binary format. MarshalProto Marshaler = iota // MarshalJSON tells the driver to send using json format. MarshalJSON ) golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlpconfig/tls.go000066400000000000000000000020131414226744000307320ustar00rootroot00000000000000// 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 otlpconfig import ( "crypto/tls" "crypto/x509" "errors" ) // CreateTLSConfig creates a tls.Config from a raw certificate bytes // to verify a server certificate. func CreateTLSConfig(certBytes []byte) (*tls.Config, 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 } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlptracetest/000077500000000000000000000000001414226744000303365ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlptracetest/client.go000066400000000000000000000062321414226744000321460ustar00rootroot00000000000000// 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 otlptracetest import ( "context" "errors" "sync" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" ) func RunExporterShutdownTest(t *testing.T, factory func() otlptrace.Client) { t.Run("testClientStopHonorsTimeout", func(t *testing.T) { testClientStopHonorsTimeout(t, factory()) }) t.Run("testClientStopHonorsCancel", func(t *testing.T) { testClientStopHonorsCancel(t, factory()) }) t.Run("testClientStopNoError", func(t *testing.T) { testClientStopNoError(t, factory()) }) t.Run("testClientStopManyTimes", func(t *testing.T) { testClientStopManyTimes(t, factory()) }) } func initializeExporter(t *testing.T, client otlptrace.Client) *otlptrace.Exporter { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e, err := otlptrace.New(ctx, client) if err != nil { t.Fatalf("failed to create exporter") } return e } func testClientStopHonorsTimeout(t *testing.T, client otlptrace.Client) { e := initializeExporter(t, client) innerCtx, innerCancel := context.WithTimeout(context.Background(), time.Microsecond) <-innerCtx.Done() if err := e.Shutdown(innerCtx); err == nil { t.Error("expected context DeadlineExceeded error, got nil") } else if !errors.Is(err, context.DeadlineExceeded) { t.Errorf("expected context DeadlineExceeded error, got %v", err) } innerCancel() } func testClientStopHonorsCancel(t *testing.T, client otlptrace.Client) { e := initializeExporter(t, client) ctx, innerCancel := context.WithCancel(context.Background()) innerCancel() if err := e.Shutdown(ctx); err == nil { t.Error("expected context canceled error, got nil") } else if !errors.Is(err, context.Canceled) { t.Errorf("expected context canceled error, got %v", err) } } func testClientStopNoError(t *testing.T, client otlptrace.Client) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e := initializeExporter(t, client) if err := e.Shutdown(ctx); err != nil { t.Errorf("shutdown errored: expected nil, got %v", err) } } func testClientStopManyTimes(t *testing.T, client otlptrace.Client) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e := initializeExporter(t, client) ch := make(chan struct{}) wg := sync.WaitGroup{} const num int = 20 wg.Add(num) errs := make([]error, num) for i := 0; i < num; i++ { go func(idx int) { defer wg.Done() <-ch errs[idx] = e.Shutdown(ctx) }(i) } close(ch) wg.Wait() for _, err := range errs { if err != nil { t.Fatalf("failed to shutdown exporter: %v", err) } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlptracetest/collector.go000066400000000000000000000061021414226744000326520ustar00rootroot00000000000000// 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 otlptracetest import ( "sort" collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" commonpb "go.opentelemetry.io/proto/otlp/common/v1" resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) // TracesCollector mocks a collector for the end-to-end testing. type TracesCollector interface { Stop() error GetResourceSpans() []*tracepb.ResourceSpans } // SpansStorage stores the spans. Mock collectors can use it to // store spans they have received. type SpansStorage struct { rsm map[string]*tracepb.ResourceSpans spanCount int } // NewSpansStorage creates a new spans storage. func NewSpansStorage() SpansStorage { return SpansStorage{ rsm: make(map[string]*tracepb.ResourceSpans), } } // AddSpans adds spans to the spans storage. func (s *SpansStorage) AddSpans(request *collectortracepb.ExportTraceServiceRequest) { for _, rs := range request.GetResourceSpans() { rstr := resourceString(rs.Resource) if existingRs, ok := s.rsm[rstr]; !ok { s.rsm[rstr] = rs // TODO (rghetia): Add support for library Info. if len(rs.InstrumentationLibrarySpans) == 0 { rs.InstrumentationLibrarySpans = []*tracepb.InstrumentationLibrarySpans{ { Spans: []*tracepb.Span{}, }, } } s.spanCount += len(rs.InstrumentationLibrarySpans[0].Spans) } else { if len(rs.InstrumentationLibrarySpans) > 0 { newSpans := rs.InstrumentationLibrarySpans[0].GetSpans() existingRs.InstrumentationLibrarySpans[0].Spans = append(existingRs.InstrumentationLibrarySpans[0].Spans, newSpans...) s.spanCount += len(newSpans) } } } } // GetSpans returns the stored spans. func (s *SpansStorage) GetSpans() []*tracepb.Span { spans := make([]*tracepb.Span, 0, s.spanCount) for _, rs := range s.rsm { spans = append(spans, rs.InstrumentationLibrarySpans[0].Spans...) } return spans } // GetResourceSpans returns the stored resource spans. func (s *SpansStorage) GetResourceSpans() []*tracepb.ResourceSpans { rss := make([]*tracepb.ResourceSpans, 0, len(s.rsm)) for _, rs := range s.rsm { rss = append(rss, rs) } return rss } func resourceString(res *resourcepb.Resource) string { sAttrs := sortedAttributes(res.GetAttributes()) rstr := "" for _, attr := range sAttrs { rstr = rstr + attr.String() } return rstr } func sortedAttributes(attrs []*commonpb.KeyValue) []*commonpb.KeyValue { sort.Slice(attrs[:], func(i, j int) bool { return attrs[i].Key < attrs[j].Key }) return attrs } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlptracetest/data.go000066400000000000000000000044351414226744000316040ustar00rootroot00000000000000// 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 otlptracetest import ( "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) // SingleReadOnlySpan returns a one-element slice with a read-only span. It // may be useful for testing driver's trace export. func SingleReadOnlySpan() []tracesdk.ReadOnlySpan { return tracetest.SpanStubs{ { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9}, SpanID: trace.SpanID{3, 4, 5, 6, 7, 8, 9, 0}, TraceFlags: trace.FlagsSampled, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9}, SpanID: trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8}, TraceFlags: trace.FlagsSampled, }), SpanKind: trace.SpanKindInternal, Name: "foo", StartTime: time.Date(2020, time.December, 8, 20, 23, 0, 0, time.UTC), EndTime: time.Date(2020, time.December, 0, 20, 24, 0, 0, time.UTC), Attributes: []attribute.KeyValue{}, Events: []tracesdk.Event{}, Links: []tracesdk.Link{}, Status: tracesdk.Status{Code: codes.Ok}, DroppedAttributes: 0, DroppedEvents: 0, DroppedLinks: 0, ChildSpanCount: 0, Resource: resource.NewSchemaless(attribute.String("a", "b")), InstrumentationLibrary: instrumentation.Library{ Name: "bar", Version: "0.0.0", }, }, }.Snapshots() } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/otlptracetest/otlptest.go000066400000000000000000000074341414226744000325530ustar00rootroot00000000000000// 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 otlptracetest import ( "context" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" commonpb "go.opentelemetry.io/proto/otlp/common/v1" ) // RunEndToEndTest can be used by otlptrace.Client tests to validate // themselves. func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlptrace.Exporter, tracesCollector TracesCollector) { pOpts := []sdktrace.TracerProviderOption{ sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher( exp, // add following two options to ensure flush sdktrace.WithBatchTimeout(5*time.Second), sdktrace.WithMaxExportBatchSize(10), ), } tp1 := sdktrace.NewTracerProvider(append(pOpts, sdktrace.WithResource(resource.NewSchemaless( attribute.String("rk1", "rv11)"), attribute.Int64("rk2", 5), )))...) tp2 := sdktrace.NewTracerProvider(append(pOpts, sdktrace.WithResource(resource.NewSchemaless( attribute.String("rk1", "rv12)"), attribute.Float64("rk3", 6.5), )))...) tr1 := tp1.Tracer("test-tracer1") tr2 := tp2.Tracer("test-tracer2") // Now create few spans m := 4 for i := 0; i < m; i++ { _, span := tr1.Start(ctx, "AlwaysSample") span.SetAttributes(attribute.Int64("i", int64(i))) span.End() _, span = tr2.Start(ctx, "AlwaysSample") span.SetAttributes(attribute.Int64("i", int64(i))) span.End() } func() { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := tp1.Shutdown(ctx); err != nil { t.Fatalf("failed to shut down a tracer provider 1: %v", err) } if err := tp2.Shutdown(ctx); err != nil { t.Fatalf("failed to shut down a tracer provider 2: %v", err) } }() // Wait >2 cycles. <-time.After(40 * time.Millisecond) // Now shutdown the exporter ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { t.Fatalf("failed to stop the exporter: %v", err) } // Shutdown the collector too so that we can begin // verification checks of expected data back. _ = tracesCollector.Stop() // Now verify that we only got two resources rss := tracesCollector.GetResourceSpans() if got, want := len(rss), 2; got != want { t.Fatalf("resource span count: got %d, want %d\n", got, want) } // Now verify spans and attributes for each resource span. for _, rs := range rss { if len(rs.InstrumentationLibrarySpans) == 0 { t.Fatalf("zero Instrumentation Library Spans") } if got, want := len(rs.InstrumentationLibrarySpans[0].Spans), m; got != want { t.Fatalf("span counts: got %d, want %d", got, want) } attrMap := map[int64]bool{} for _, s := range rs.InstrumentationLibrarySpans[0].Spans { if gotName, want := s.Name, "AlwaysSample"; gotName != want { t.Fatalf("span name: got %s, want %s", gotName, want) } attrMap[s.Attributes[0].Value.Value.(*commonpb.AnyValue_IntValue).IntValue] = true } if got, want := len(attrMap), m; got != want { t.Fatalf("span attribute unique values: got %d want %d", got, want) } for i := 0; i < m; i++ { _, ok := attrMap[int64(i)] if !ok { t.Fatalf("span with attribute %d missing", i) } } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/retry/000077500000000000000000000000001414226744000266065ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/retry/retry.go000066400000000000000000000074311414226744000303070ustar00rootroot00000000000000// 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 retry import ( "context" "fmt" "time" "github.com/cenkalti/backoff/v4" ) // DefaultConfig are the recommended defaults to use. var DefaultConfig = Config{ Enabled: true, InitialInterval: 5 * time.Second, MaxInterval: 30 * time.Second, MaxElapsedTime: time.Minute, } // Config defines configuration for retrying batches in case of export failure // using an exponential backoff. type Config struct { // Enabled indicates whether to not retry sending batches in case of // export failure. Enabled bool // InitialInterval the time to wait after the first failure before // retrying. InitialInterval time.Duration // MaxInterval is the upper bound on backoff interval. Once this value is // reached the delay between consecutive retries will always be // `MaxInterval`. MaxInterval time.Duration // MaxElapsedTime is the maximum amount of time (including retries) spent // trying to send a request/batch. Once this value is reached, the data // is discarded. MaxElapsedTime time.Duration } // RequestFunc wraps a request with retry logic. type RequestFunc func(context.Context, func(context.Context) error) error // EvaluateFunc returns if an error is retry-able and if an explicit throttle // duration should be honored that was included in the error. type EvaluateFunc func(error) (bool, time.Duration) func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc { if !c.Enabled { return func(ctx context.Context, fn func(context.Context) error) error { return fn(ctx) } } // Do not use NewExponentialBackOff since it calls Reset and the code here // must call Reset after changing the InitialInterval (this saves an // unnecessary call to Now). b := &backoff.ExponentialBackOff{ InitialInterval: c.InitialInterval, RandomizationFactor: backoff.DefaultRandomizationFactor, Multiplier: backoff.DefaultMultiplier, MaxInterval: c.MaxInterval, MaxElapsedTime: c.MaxElapsedTime, Stop: backoff.Stop, Clock: backoff.SystemClock, } b.Reset() return func(ctx context.Context, fn func(context.Context) error) error { for { err := fn(ctx) if err == nil { return nil } retryable, throttle := evaluate(err) if !retryable { return err } bOff := b.NextBackOff() if bOff == backoff.Stop { return fmt.Errorf("max retry time elapsed: %w", err) } // Wait for the greater of the backoff or throttle delay. var delay time.Duration if bOff > throttle { delay = bOff } else { elapsed := b.GetElapsedTime() if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime { return fmt.Errorf("max retry time would elapse: %w", err) } delay = throttle } if err := waitFunc(ctx, delay); err != nil { return err } } } } // Allow override for testing. var waitFunc = wait func wait(ctx context.Context, delay time.Duration) error { timer := time.NewTimer(delay) defer timer.Stop() select { case <-ctx.Done(): // Handle the case where the timer and context deadline end // simultaneously by prioritizing the timer expiration nil value // response. select { case <-timer.C: default: return ctx.Err() } case <-timer.C: } return nil } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/retry/retry_test.go000066400000000000000000000120561414226744000313450ustar00rootroot00000000000000// 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 retry import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" ) func TestWait(t *testing.T) { tests := []struct { ctx context.Context delay time.Duration expected error }{ { ctx: context.Background(), delay: time.Duration(0), expected: nil, }, { ctx: context.Background(), delay: time.Duration(1), expected: nil, }, { ctx: context.Background(), delay: time.Duration(-1), expected: nil, }, { ctx: func() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx }(), // Ensure the timer and context do not end simultaneously. delay: 1 * time.Hour, expected: context.Canceled, }, } for _, test := range tests { assert.Equal(t, test.expected, wait(test.ctx, test.delay)) } } func TestNonRetryableError(t *testing.T) { ev := func(error) (bool, time.Duration) { return false, 0 } reqFunc := Config{ Enabled: true, InitialInterval: 1 * time.Nanosecond, MaxInterval: 1 * time.Nanosecond, // Never stop retrying. MaxElapsedTime: 0, }.RequestFunc(ev) ctx := context.Background() assert.NoError(t, reqFunc(ctx, func(context.Context) error { return nil })) assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error { return assert.AnError }), assert.AnError) } func TestThrottledRetry(t *testing.T) { // Ensure the throttle delay is used by making longer than backoff delay. throttleDelay, backoffDelay := time.Second, time.Nanosecond ev := func(error) (bool, time.Duration) { // Retry everything with a throttle delay. return true, throttleDelay } reqFunc := Config{ Enabled: true, InitialInterval: backoffDelay, MaxInterval: backoffDelay, // Never stop retrying. MaxElapsedTime: 0, }.RequestFunc(ev) origWait := waitFunc var done bool waitFunc = func(_ context.Context, delay time.Duration) error { assert.Equal(t, throttleDelay, delay, "retry not throttled") // Try twice to ensure call is attempted again after delay. if done { return assert.AnError } done = true return nil } defer func() { waitFunc = origWait }() ctx := context.Background() assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error { return errors.New("not this error") }), assert.AnError) } func TestBackoffRetry(t *testing.T) { ev := func(error) (bool, time.Duration) { return true, 0 } delay := time.Nanosecond reqFunc := Config{ Enabled: true, InitialInterval: delay, MaxInterval: delay, // Never stop retrying. MaxElapsedTime: 0, }.RequestFunc(ev) origWait := waitFunc var done bool waitFunc = func(_ context.Context, d time.Duration) error { assert.Equal(t, delay, d, "retry not backoffed") // Try twice to ensure call is attempted again after delay. if done { return assert.AnError } done = true return nil } defer func() { waitFunc = origWait }() ctx := context.Background() assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error { return errors.New("not this error") }), assert.AnError) } func TestThrottledRetryGreaterThanMaxElapsedTime(t *testing.T) { // Ensure the throttle delay is used by making longer than backoff delay. tDelay, bDelay := time.Hour, time.Nanosecond ev := func(error) (bool, time.Duration) { return true, tDelay } reqFunc := Config{ Enabled: true, InitialInterval: bDelay, MaxInterval: bDelay, MaxElapsedTime: tDelay - (time.Nanosecond), }.RequestFunc(ev) ctx := context.Background() assert.Contains(t, reqFunc(ctx, func(context.Context) error { return assert.AnError }).Error(), "max retry time would elapse: ") } func TestMaxElapsedTime(t *testing.T) { ev := func(error) (bool, time.Duration) { return true, 0 } delay := time.Nanosecond reqFunc := Config{ Enabled: true, // InitialInterval > MaxElapsedTime means immediate return. InitialInterval: 2 * delay, MaxElapsedTime: delay, }.RequestFunc(ev) ctx := context.Background() assert.Contains(t, reqFunc(ctx, func(context.Context) error { return assert.AnError }).Error(), "max retry time elapsed: ") } func TestRetryNotEnabled(t *testing.T) { ev := func(error) (bool, time.Duration) { t.Error("evaluated retry when not enabled") return false, 0 } reqFunc := Config{}.RequestFunc(ev) ctx := context.Background() assert.NoError(t, reqFunc(ctx, func(context.Context) error { return nil })) assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error { return assert.AnError }), assert.AnError) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/000077500000000000000000000000001414226744000304735ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/attribute.go000066400000000000000000000101071414226744000330240ustar00rootroot00000000000000// 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 tracetransform import ( "go.opentelemetry.io/otel/attribute" commonpb "go.opentelemetry.io/proto/otlp/common/v1" "go.opentelemetry.io/otel/sdk/resource" ) // KeyValues transforms a slice of attribute KeyValues into OTLP key-values. func KeyValues(attrs []attribute.KeyValue) []*commonpb.KeyValue { if len(attrs) == 0 { return nil } out := make([]*commonpb.KeyValue, 0, len(attrs)) for _, kv := range attrs { out = append(out, KeyValue(kv)) } return out } // Iterator transforms an attribute iterator into OTLP key-values. func Iterator(iter attribute.Iterator) []*commonpb.KeyValue { l := iter.Len() if l == 0 { return nil } out := make([]*commonpb.KeyValue, 0, l) for iter.Next() { out = append(out, KeyValue(iter.Attribute())) } return out } // ResourceAttributes transforms a Resource OTLP key-values. func ResourceAttributes(resource *resource.Resource) []*commonpb.KeyValue { return Iterator(resource.Iter()) } // KeyValue transforms an attribute KeyValue into an OTLP key-value. func KeyValue(kv attribute.KeyValue) *commonpb.KeyValue { return &commonpb.KeyValue{Key: string(kv.Key), Value: Value(kv.Value)} } // Value transforms an attribute Value into an OTLP AnyValue. func Value(v attribute.Value) *commonpb.AnyValue { av := new(commonpb.AnyValue) switch v.Type() { case attribute.BOOL: av.Value = &commonpb.AnyValue_BoolValue{ BoolValue: v.AsBool(), } case attribute.BOOLSLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: boolSliceValues(v.AsBoolSlice()), }, } case attribute.INT64: av.Value = &commonpb.AnyValue_IntValue{ IntValue: v.AsInt64(), } case attribute.INT64SLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: int64SliceValues(v.AsInt64Slice()), }, } case attribute.FLOAT64: av.Value = &commonpb.AnyValue_DoubleValue{ DoubleValue: v.AsFloat64(), } case attribute.FLOAT64SLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: float64SliceValues(v.AsFloat64Slice()), }, } case attribute.STRING: av.Value = &commonpb.AnyValue_StringValue{ StringValue: v.AsString(), } case attribute.STRINGSLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: stringSliceValues(v.AsStringSlice()), }, } default: av.Value = &commonpb.AnyValue_StringValue{ StringValue: "INVALID", } } return av } func boolSliceValues(vals []bool) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: v, }, } } return converted } func int64SliceValues(vals []int64) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: v, }, } } return converted } func float64SliceValues(vals []float64) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: v, }, } } return converted } func stringSliceValues(vals []string) []*commonpb.AnyValue { converted := make([]*commonpb.AnyValue, len(vals)) for i, v := range vals { converted[i] = &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: v, }, } } return converted } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/attribute_test.go000066400000000000000000000152741414226744000340750ustar00rootroot00000000000000// 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 tracetransform import ( "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" commonpb "go.opentelemetry.io/proto/otlp/common/v1" ) type attributeTest struct { attrs []attribute.KeyValue expected []*commonpb.KeyValue } func TestAttributes(t *testing.T) { for _, test := range []attributeTest{ {nil, nil}, { []attribute.KeyValue{ attribute.Int("int to int", 123), attribute.Int64("int64 to int64", 1234567), attribute.Float64("float64 to double", 1.61), attribute.String("string to string", "string"), attribute.Bool("bool to bool", true), }, []*commonpb.KeyValue{ { Key: "int to int", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 123, }, }, }, { Key: "int64 to int64", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 1234567, }, }, }, { Key: "float64 to double", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: 1.61, }, }, }, { Key: "string to string", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "string", }, }, }, { Key: "bool to bool", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: true, }, }, }, }, }, } { got := KeyValues(test.attrs) if !assert.Len(t, got, len(test.expected)) { continue } for i, actual := range got { if a, ok := actual.Value.Value.(*commonpb.AnyValue_DoubleValue); ok { e, ok := test.expected[i].Value.Value.(*commonpb.AnyValue_DoubleValue) if !ok { t.Errorf("expected AnyValue_DoubleValue, got %T", test.expected[i].Value.Value) continue } if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { continue } e.DoubleValue = a.DoubleValue } assert.Equal(t, test.expected[i], actual) } } } func TestArrayAttributes(t *testing.T) { // Array KeyValue supports only arrays of primitive types: // "bool", "int", "int64", // "float64", "string", for _, test := range []attributeTest{ {nil, nil}, { []attribute.KeyValue{ { Key: attribute.Key("invalid"), Value: attribute.Value{}, }, }, []*commonpb.KeyValue{ { Key: "invalid", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "INVALID", }, }, }, }, }, { []attribute.KeyValue{ attribute.BoolSlice("bool slice to bool array", []bool{true, false}), attribute.IntSlice("int slice to int64 array", []int{1, 2, 3}), attribute.Int64Slice("int64 slice to int64 array", []int64{1, 2, 3}), attribute.Float64Slice("float64 slice to double array", []float64{1.11, 2.22, 3.33}), attribute.StringSlice("string slice to string array", []string{"foo", "bar", "baz"}), }, []*commonpb.KeyValue{ newOTelBoolArray("bool slice to bool array", []bool{true, false}), newOTelIntArray("int slice to int64 array", []int64{1, 2, 3}), newOTelIntArray("int64 slice to int64 array", []int64{1, 2, 3}), newOTelDoubleArray("float64 slice to double array", []float64{1.11, 2.22, 3.33}), newOTelStringArray("string slice to string array", []string{"foo", "bar", "baz"}), }, }, } { actualArrayAttributes := KeyValues(test.attrs) expectedArrayAttributes := test.expected if !assert.Len(t, actualArrayAttributes, len(expectedArrayAttributes)) { continue } for i, actualArrayAttr := range actualArrayAttributes { expectedArrayAttr := expectedArrayAttributes[i] expectedKey, actualKey := expectedArrayAttr.Key, actualArrayAttr.Key if !assert.Equal(t, expectedKey, actualKey) { continue } expected := expectedArrayAttr.Value.GetArrayValue() actual := actualArrayAttr.Value.GetArrayValue() if expected == nil { assert.Nil(t, actual) continue } if assert.NotNil(t, actual, "expected not nil for %s", actualKey) { assertExpectedArrayValues(t, expected.Values, actual.Values) } } } } func assertExpectedArrayValues(t *testing.T, expectedValues, actualValues []*commonpb.AnyValue) { for i, actual := range actualValues { expected := expectedValues[i] if a, ok := actual.Value.(*commonpb.AnyValue_DoubleValue); ok { e, ok := expected.Value.(*commonpb.AnyValue_DoubleValue) if !ok { t.Errorf("expected AnyValue_DoubleValue, got %T", expected.Value) continue } if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { continue } e.DoubleValue = a.DoubleValue } assert.Equal(t, expected, actual) } } func newOTelBoolArray(key string, values []bool) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, b := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: b, }, }) } return newOTelArray(key, arrayValues) } func newOTelIntArray(key string, values []int64) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, i := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: i, }, }) } return newOTelArray(key, arrayValues) } func newOTelDoubleArray(key string, values []float64) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, d := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: d, }, }) } return newOTelArray(key, arrayValues) } func newOTelStringArray(key string, values []string) *commonpb.KeyValue { arrayValues := []*commonpb.AnyValue{} for _, s := range values { arrayValues = append(arrayValues, &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: s, }, }) } return newOTelArray(key, arrayValues) } func newOTelArray(key string, arrayValues []*commonpb.AnyValue) *commonpb.KeyValue { return &commonpb.KeyValue{ Key: key, Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ Values: arrayValues, }, }, }, } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/instrumentation.go000066400000000000000000000017151414226744000342710ustar00rootroot00000000000000// 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 tracetransform import ( commonpb "go.opentelemetry.io/proto/otlp/common/v1" "go.opentelemetry.io/otel/sdk/instrumentation" ) func InstrumentationLibrary(il instrumentation.Library) *commonpb.InstrumentationLibrary { if il == (instrumentation.Library{}) { return nil } return &commonpb.InstrumentationLibrary{ Name: il.Name, Version: il.Version, } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/resource.go000066400000000000000000000016621414226744000326560ustar00rootroot00000000000000// 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 tracetransform import ( resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" "go.opentelemetry.io/otel/sdk/resource" ) // Resource transforms a Resource into an OTLP Resource. func Resource(r *resource.Resource) *resourcepb.Resource { if r == nil { return nil } return &resourcepb.Resource{Attributes: ResourceAttributes(r)} } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/resource_test.go000066400000000000000000000025541414226744000337160ustar00rootroot00000000000000// 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 tracetransform import ( "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" ) func TestNilResource(t *testing.T) { assert.Empty(t, Resource(nil)) } func TestEmptyResource(t *testing.T) { assert.Empty(t, Resource(&resource.Resource{})) } /* * This does not include any testing on the ordering of Resource Attributes. * They are stored as a map internally to the Resource and their order is not * guaranteed. */ func TestResourceAttributes(t *testing.T) { attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} got := Resource(resource.NewSchemaless(attrs...)).GetAttributes() if !assert.Len(t, attrs, 2) { return } assert.ElementsMatch(t, KeyValues(attrs), got) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/span.go000066400000000000000000000140071414226744000317650ustar00rootroot00000000000000// 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 tracetransform import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" "go.opentelemetry.io/otel/sdk/instrumentation" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" ) const ( maxEventsPerSpan = 128 ) // Spans transforms a slice of OpenTelemetry spans into a slice of OTLP // ResourceSpans. func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans { if len(sdl) == 0 { return nil } rsm := make(map[attribute.Distinct]*tracepb.ResourceSpans) type ilsKey struct { r attribute.Distinct il instrumentation.Library } ilsm := make(map[ilsKey]*tracepb.InstrumentationLibrarySpans) var resources int for _, sd := range sdl { if sd == nil { continue } rKey := sd.Resource().Equivalent() iKey := ilsKey{ r: rKey, il: sd.InstrumentationLibrary(), } ils, iOk := ilsm[iKey] if !iOk { // Either the resource or instrumentation library were unknown. ils = &tracepb.InstrumentationLibrarySpans{ InstrumentationLibrary: InstrumentationLibrary(sd.InstrumentationLibrary()), Spans: []*tracepb.Span{}, SchemaUrl: sd.InstrumentationLibrary().SchemaURL, } } ils.Spans = append(ils.Spans, span(sd)) ilsm[iKey] = ils rs, rOk := rsm[rKey] if !rOk { resources++ // The resource was unknown. rs = &tracepb.ResourceSpans{ Resource: Resource(sd.Resource()), InstrumentationLibrarySpans: []*tracepb.InstrumentationLibrarySpans{ils}, SchemaUrl: sd.Resource().SchemaURL(), } rsm[rKey] = rs continue } // The resource has been seen before. Check if the instrumentation // library lookup was unknown because if so we need to add it to the // ResourceSpans. Otherwise, the instrumentation library has already // been seen and the append we did above will be included it in the // InstrumentationLibrarySpans reference. if !iOk { rs.InstrumentationLibrarySpans = append(rs.InstrumentationLibrarySpans, ils) } } // Transform the categorized map into a slice rss := make([]*tracepb.ResourceSpans, 0, resources) for _, rs := range rsm { rss = append(rss, rs) } return rss } // span transforms a Span into an OTLP span. func span(sd tracesdk.ReadOnlySpan) *tracepb.Span { if sd == nil { return nil } tid := sd.SpanContext().TraceID() sid := sd.SpanContext().SpanID() s := &tracepb.Span{ TraceId: tid[:], SpanId: sid[:], TraceState: sd.SpanContext().TraceState().String(), Status: status(sd.Status().Code, sd.Status().Description), StartTimeUnixNano: uint64(sd.StartTime().UnixNano()), EndTimeUnixNano: uint64(sd.EndTime().UnixNano()), Links: links(sd.Links()), Kind: spanKind(sd.SpanKind()), Name: sd.Name(), Attributes: KeyValues(sd.Attributes()), Events: spanEvents(sd.Events()), DroppedAttributesCount: uint32(sd.DroppedAttributes()), DroppedEventsCount: uint32(sd.DroppedEvents()), DroppedLinksCount: uint32(sd.DroppedLinks()), } if psid := sd.Parent().SpanID(); psid.IsValid() { s.ParentSpanId = psid[:] } return s } // status transform a span code and message into an OTLP span status. func status(status codes.Code, message string) *tracepb.Status { var c tracepb.Status_StatusCode switch status { case codes.Ok: c = tracepb.Status_STATUS_CODE_OK case codes.Error: c = tracepb.Status_STATUS_CODE_ERROR default: c = tracepb.Status_STATUS_CODE_UNSET } return &tracepb.Status{ Code: c, Message: message, } } // links transforms span Links to OTLP span links. func links(links []tracesdk.Link) []*tracepb.Span_Link { if len(links) == 0 { return nil } sl := make([]*tracepb.Span_Link, 0, len(links)) for _, otLink := range links { // This redefinition is necessary to prevent otLink.*ID[:] copies // being reused -- in short we need a new otLink per iteration. otLink := otLink tid := otLink.SpanContext.TraceID() sid := otLink.SpanContext.SpanID() sl = append(sl, &tracepb.Span_Link{ TraceId: tid[:], SpanId: sid[:], Attributes: KeyValues(otLink.Attributes), }) } return sl } // spanEvents transforms span Events to an OTLP span events. func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event { if len(es) == 0 { return nil } evCount := len(es) if evCount > maxEventsPerSpan { evCount = maxEventsPerSpan } events := make([]*tracepb.Span_Event, 0, evCount) nEvents := 0 // Transform message events for _, e := range es { if nEvents >= maxEventsPerSpan { break } nEvents++ events = append(events, &tracepb.Span_Event{ Name: e.Name, TimeUnixNano: uint64(e.Time.UnixNano()), Attributes: KeyValues(e.Attributes), // TODO (rghetia) : Add Drop Counts when supported. }, ) } return events } // spanKind transforms a SpanKind to an OTLP span kind. func spanKind(kind trace.SpanKind) tracepb.Span_SpanKind { switch kind { case trace.SpanKindInternal: return tracepb.Span_SPAN_KIND_INTERNAL case trace.SpanKindClient: return tracepb.Span_SPAN_KIND_CLIENT case trace.SpanKindServer: return tracepb.Span_SPAN_KIND_SERVER case trace.SpanKindProducer: return tracepb.Span_SPAN_KIND_PRODUCER case trace.SpanKindConsumer: return tracepb.Span_SPAN_KIND_CONSUMER default: return tracepb.Span_SPAN_KIND_UNSPECIFIED } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/internal/tracetransform/span_test.go000066400000000000000000000236741414226744000330360ustar00rootroot00000000000000// 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 tracetransform import ( "strconv" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) func TestSpanKind(t *testing.T) { for _, test := range []struct { kind trace.SpanKind expected tracepb.Span_SpanKind }{ { trace.SpanKindInternal, tracepb.Span_SPAN_KIND_INTERNAL, }, { trace.SpanKindClient, tracepb.Span_SPAN_KIND_CLIENT, }, { trace.SpanKindServer, tracepb.Span_SPAN_KIND_SERVER, }, { trace.SpanKindProducer, tracepb.Span_SPAN_KIND_PRODUCER, }, { trace.SpanKindConsumer, tracepb.Span_SPAN_KIND_CONSUMER, }, { trace.SpanKind(-1), tracepb.Span_SPAN_KIND_UNSPECIFIED, }, } { assert.Equal(t, test.expected, spanKind(test.kind)) } } func TestNilSpanEvent(t *testing.T) { assert.Nil(t, spanEvents(nil)) } func TestEmptySpanEvent(t *testing.T) { assert.Nil(t, spanEvents([]tracesdk.Event{})) } func TestSpanEvent(t *testing.T) { attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} eventTime := time.Date(2020, 5, 20, 0, 0, 0, 0, time.UTC) got := spanEvents([]tracesdk.Event{ { Name: "test 1", Attributes: []attribute.KeyValue{}, Time: eventTime, }, { Name: "test 2", Attributes: attrs, Time: eventTime, }, }) if !assert.Len(t, got, 2) { return } eventTimestamp := uint64(1589932800 * 1e9) assert.Equal(t, &tracepb.Span_Event{Name: "test 1", Attributes: nil, TimeUnixNano: eventTimestamp}, got[0]) // Do not test Attributes directly, just that the return value goes to the correct field. assert.Equal(t, &tracepb.Span_Event{Name: "test 2", Attributes: KeyValues(attrs), TimeUnixNano: eventTimestamp}, got[1]) } func TestExcessiveSpanEvents(t *testing.T) { e := make([]tracesdk.Event, maxEventsPerSpan+1) for i := 0; i < maxEventsPerSpan+1; i++ { e[i] = tracesdk.Event{Name: strconv.Itoa(i)} } assert.Len(t, e, maxEventsPerSpan+1) got := spanEvents(e) assert.Len(t, got, maxEventsPerSpan) // Ensure the drop order. assert.Equal(t, strconv.Itoa(maxEventsPerSpan-1), got[len(got)-1].Name) } func TestNilLinks(t *testing.T) { assert.Nil(t, links(nil)) } func TestEmptyLinks(t *testing.T) { assert.Nil(t, links([]tracesdk.Link{})) } func TestLinks(t *testing.T) { attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} l := []tracesdk.Link{ {}, { SpanContext: trace.SpanContext{}, Attributes: attrs, }, } got := links(l) // Make sure we get the same number back first. if !assert.Len(t, got, 2) { return } // Empty should be empty. expected := &tracepb.Span_Link{ TraceId: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, SpanId: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, } assert.Equal(t, expected, got[0]) // Do not test Attributes directly, just that the return value goes to the correct field. expected.Attributes = KeyValues(attrs) assert.Equal(t, expected, got[1]) // Changes to our links should not change the produced links. l[1].SpanContext = l[1].SpanContext.WithTraceID(trace.TraceID{}) assert.Equal(t, expected, got[1]) } func TestStatus(t *testing.T) { for _, test := range []struct { code codes.Code message string otlpStatus tracepb.Status_StatusCode }{ { codes.Ok, "test Ok", tracepb.Status_STATUS_CODE_OK, }, { codes.Unset, "test Unset", tracepb.Status_STATUS_CODE_UNSET, }, { message: "default code is unset", otlpStatus: tracepb.Status_STATUS_CODE_UNSET, }, { codes.Error, "test Error", tracepb.Status_STATUS_CODE_ERROR, }, } { expected := &tracepb.Status{Code: test.otlpStatus, Message: test.message} assert.Equal(t, expected, status(test.code, test.message)) } } func TestNilSpan(t *testing.T) { assert.Nil(t, span(nil)) } func TestNilSpanData(t *testing.T) { assert.Nil(t, Spans(nil)) } func TestEmptySpanData(t *testing.T) { assert.Nil(t, Spans(nil)) } func TestSpanData(t *testing.T) { // Full test of span data // March 31, 2020 5:01:26 1234nanos (UTC) startTime := time.Unix(1585674086, 1234) endTime := startTime.Add(10 * time.Second) traceState, _ := trace.ParseTraceState("key1=val1,key2=val2") spanData := tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, TraceState: traceState, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}, TraceState: traceState, Remote: true, }), SpanKind: trace.SpanKindServer, Name: "span data to span data", StartTime: startTime, EndTime: endTime, Events: []tracesdk.Event{ {Time: startTime, Attributes: []attribute.KeyValue{ attribute.Int64("CompressedByteSize", 512), }, }, {Time: endTime, Attributes: []attribute.KeyValue{ attribute.String("EventType", "Recv"), }, }, }, Links: []tracesdk.Link{ { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF}, SpanID: trace.SpanID{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, TraceFlags: 0, }), Attributes: []attribute.KeyValue{ attribute.String("LinkType", "Parent"), }, DroppedAttributeCount: 0, }, { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}, SpanID: trace.SpanID{0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7}, TraceFlags: 0, }), Attributes: []attribute.KeyValue{ attribute.String("LinkType", "Child"), }, DroppedAttributeCount: 0, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "utterly unrecognized", }, Attributes: []attribute.KeyValue{ attribute.Int64("timeout_ns", 12e9), }, DroppedAttributes: 1, DroppedEvents: 2, DroppedLinks: 3, Resource: resource.NewWithAttributes( "http://example.com/custom-resource-schema", attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5), attribute.StringSlice("rk3", []string{"sv1", "sv2"}), ), InstrumentationLibrary: instrumentation.Library{ Name: "go.opentelemetry.io/test/otel", Version: "v0.0.1", SchemaURL: semconv.SchemaURL, }, } // Not checking resource as the underlying map of our Resource makes // ordering impossible to guarantee on the output. The Resource // transform function has unit tests that should suffice. expectedSpan := &tracepb.Span{ TraceId: []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanId: []byte{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, ParentSpanId: []byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}, TraceState: "key1=val1,key2=val2", Name: spanData.Name, Kind: tracepb.Span_SPAN_KIND_SERVER, StartTimeUnixNano: uint64(startTime.UnixNano()), EndTimeUnixNano: uint64(endTime.UnixNano()), Status: status(spanData.Status.Code, spanData.Status.Description), Events: spanEvents(spanData.Events), Links: links(spanData.Links), Attributes: KeyValues(spanData.Attributes), DroppedAttributesCount: 1, DroppedEventsCount: 2, DroppedLinksCount: 3, } got := Spans(tracetest.SpanStubs{spanData}.Snapshots()) require.Len(t, got, 1) assert.Equal(t, got[0].GetResource(), Resource(spanData.Resource)) assert.Equal(t, got[0].SchemaUrl, spanData.Resource.SchemaURL()) ilSpans := got[0].GetInstrumentationLibrarySpans() require.Len(t, ilSpans, 1) assert.Equal(t, ilSpans[0].SchemaUrl, spanData.InstrumentationLibrary.SchemaURL) assert.Equal(t, ilSpans[0].GetInstrumentationLibrary(), InstrumentationLibrary(spanData.InstrumentationLibrary)) require.Len(t, ilSpans[0].Spans, 1) actualSpan := ilSpans[0].Spans[0] if diff := cmp.Diff(expectedSpan, actualSpan, cmp.Comparer(proto.Equal)); diff != "" { t.Fatalf("transformed span differs %v\n", diff) } } // Empty parent span ID should be treated as root span. func TestRootSpanData(t *testing.T) { sd := Spans(tracetest.SpanStubs{{}}.Snapshots()) require.Len(t, sd, 1) rs := sd[0] got := rs.GetInstrumentationLibrarySpans()[0].GetSpans()[0].GetParentSpanId() // Empty means root span. assert.Nil(t, got, "incorrect transform of root parent span ID") } func TestSpanDataNilResource(t *testing.T) { assert.NotPanics(t, func() { Spans(tracetest.SpanStubs{{}}.Snapshots()) }) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/000077500000000000000000000000001414226744000264765ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/client.go000066400000000000000000000060071414226744000303060ustar00rootroot00000000000000// 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 otlptracegrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" import ( "context" "errors" "fmt" "sync" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/connection" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "google.golang.org/grpc" coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) type client struct { connection *connection.Connection lock sync.Mutex tracesClient coltracepb.TraceServiceClient } var _ otlptrace.Client = (*client)(nil) var ( errNoClient = errors.New("no client") ) // NewClient creates a new gRPC trace client. func NewClient(opts ...Option) otlptrace.Client { cfg := otlpconfig.NewDefaultConfig() otlpconfig.ApplyGRPCEnvConfigs(&cfg) for _, opt := range opts { opt.applyGRPCOption(&cfg) } c := &client{} c.connection = connection.NewConnection(cfg, cfg.Traces, c.handleNewConnection) return c } func (c *client) handleNewConnection(cc *grpc.ClientConn) { c.lock.Lock() defer c.lock.Unlock() if cc != nil { c.tracesClient = coltracepb.NewTraceServiceClient(cc) } else { c.tracesClient = nil } } // Start establishes a connection to the collector. func (c *client) Start(ctx context.Context) error { return c.connection.StartConnection(ctx) } // Stop shuts down the connection to the collector. func (c *client) Stop(ctx context.Context) error { return c.connection.Shutdown(ctx) } // UploadTraces sends a batch of spans to the collector. func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error { if !c.connection.Connected() { return fmt.Errorf("traces exporter is disconnected from the server %s: %w", c.connection.SCfg.Endpoint, c.connection.LastConnectError()) } ctx, cancel := c.connection.ContextWithStop(ctx) defer cancel() ctx, tCancel := context.WithTimeout(ctx, c.connection.SCfg.Timeout) defer tCancel() ctx = c.connection.ContextWithMetadata(ctx) err := func() error { c.lock.Lock() defer c.lock.Unlock() if c.tracesClient == nil { return errNoClient } return c.connection.DoRequest(ctx, func(ctx context.Context) error { _, err := c.tracesClient.Export(ctx, &coltracepb.ExportTraceServiceRequest{ ResourceSpans: protoSpans, }) return err }) }() if err != nil { c.connection.SetStateDisconnected(err) } return err } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/client_test.go000066400000000000000000000361631414226744000313530ustar00rootroot00000000000000// 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 otlptracegrpc_test import ( "context" "fmt" "net" "strings" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlptracetest" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" commonpb "go.opentelemetry.io/proto/otlp/common/v1" ) var roSpans = tracetest.SpanStubs{{Name: "Span 0"}}.Snapshots() func TestNew_endToEnd(t *testing.T) { tests := []struct { name string additionalOpts []otlptracegrpc.Option }{ { name: "StandardExporter", }, { name: "WithCompressor", additionalOpts: []otlptracegrpc.Option{ otlptracegrpc.WithCompressor(gzip.Name), }, }, { name: "WithServiceConfig", additionalOpts: []otlptracegrpc.Option{ otlptracegrpc.WithServiceConfig("{}"), }, }, { name: "WithDialOptions", additionalOpts: []otlptracegrpc.Option{ otlptracegrpc.WithDialOption(grpc.WithBlock()), }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { newExporterEndToEndTest(t, test.additionalOpts) }) } } func newGRPCExporter(t *testing.T, ctx context.Context, endpoint string, additionalOpts ...otlptracegrpc.Option) *otlptrace.Exporter { opts := []otlptracegrpc.Option{ otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint(endpoint), otlptracegrpc.WithReconnectionPeriod(50 * time.Millisecond), } opts = append(opts, additionalOpts...) client := otlptracegrpc.NewClient(opts...) exp, err := otlptrace.New(ctx, client) if err != nil { t.Fatalf("failed to create a new collector exporter: %v", err) } return exp } func newExporterEndToEndTest(t *testing.T, additionalOpts []otlptracegrpc.Option) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, additionalOpts...) defer func() { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { panic(err) } }() otlptracetest.RunEndToEndTest(ctx, t, exp, mc) } func TestExporterShutdown(t *testing.T) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) otlptracetest.RunExporterShutdownTest(t, func() otlptrace.Client { return otlptracegrpc.NewClient( otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint(mc.endpoint), otlptracegrpc.WithReconnectionPeriod(50*time.Millisecond)) }) } func TestNew_invokeStartThenStopManyTimes(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { if err := exp.Shutdown(ctx); err != nil { panic(err) } }() // Invoke Start numerous times, should return errAlreadyStarted for i := 0; i < 10; i++ { if err := exp.Start(ctx); err == nil || !strings.Contains(err.Error(), "already started") { t.Fatalf("#%d unexpected Start error: %v", i, err) } } if err := exp.Shutdown(ctx); err != nil { t.Fatalf("failed to Shutdown the exporter: %v", err) } // Invoke Shutdown numerous times for i := 0; i < 10; i++ { if err := exp.Shutdown(ctx); err != nil { t.Fatalf(`#%d got error (%v) expected none`, i, err) } } } func TestNew_collectorConnectionDiesThenReconnectsWhenInRestMode(t *testing.T) { mc := runMockCollector(t) reconnectionPeriod := 20 * time.Millisecond ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlptracegrpc.WithRetry(otlptracegrpc.RetryConfig{Enabled: false}), otlptracegrpc.WithReconnectionPeriod(reconnectionPeriod)) defer func() { require.NoError(t, exp.Shutdown(ctx)) }() // Wait for a connection. mc.ln.WaitForConn() // We'll now stop the collector right away to simulate a connection // dying in the midst of communication or even not existing before. require.NoError(t, mc.stop()) // first export, it will send disconnected message to the channel on export failure, // trigger almost immediate reconnection require.Error(t, exp.ExportSpans(ctx, roSpans)) // second export, it will detect connection issue, change state of exporter to disconnected and // send message to disconnected channel but this time reconnection gouroutine will be in (rest mode, not listening to the disconnected channel) require.Error(t, exp.ExportSpans(ctx, roSpans)) // as a result we have exporter in disconnected state waiting for disconnection message to reconnect // resurrect collector nmc := runMockCollectorAtEndpoint(t, mc.endpoint) // make sure reconnection loop hits beginning and goes back to waiting mode // after hitting beginning of the loop it should reconnect nmc.ln.WaitForConn() n := 10 for i := 0; i < n; i++ { // when disconnected exp.ExportSpans doesnt send disconnected messages again // it just quits and return last connection error require.NoError(t, exp.ExportSpans(ctx, roSpans)) } nmaSpans := nmc.getSpans() // Expecting 10 spans that were sampled, given that if g, w := len(nmaSpans), n; g != w { t.Fatalf("Connected collector: spans: got %d want %d", g, w) } dSpans := mc.getSpans() // Expecting 0 spans to have been received by the original but now dead collector if g, w := len(dSpans), 0; g != w { t.Fatalf("Disconnected collector: spans: got %d want %d", g, w) } require.NoError(t, nmc.Stop()) } func TestNew_collectorConnectionDiesThenReconnects(t *testing.T) { mc := runMockCollector(t) reconnectionPeriod := 50 * time.Millisecond ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlptracegrpc.WithRetry(otlptracegrpc.RetryConfig{Enabled: false}), otlptracegrpc.WithReconnectionPeriod(reconnectionPeriod)) defer func() { require.NoError(t, exp.Shutdown(ctx)) }() mc.ln.WaitForConn() // We'll now stop the collector right away to simulate a connection // dying in the midst of communication or even not existing before. require.NoError(t, mc.stop()) // In the test below, we'll stop the collector many times, // while exporting traces and test to ensure that we can // reconnect. for j := 0; j < 3; j++ { // No endpoint up. require.Error(t, exp.ExportSpans(ctx, roSpans)) // Now resurrect the collector by making a new one but reusing the // old endpoint, and the collector should reconnect automatically. nmc := runMockCollectorAtEndpoint(t, mc.endpoint) // Give the exporter sometime to reconnect nmc.ln.WaitForConn() n := 10 for i := 0; i < n; i++ { require.NoError(t, exp.ExportSpans(ctx, roSpans)) } nmaSpans := nmc.getSpans() // Expecting 10 spans that were sampled, given that if g, w := len(nmaSpans), n; g != w { t.Fatalf("Round #%d: Connected collector: spans: got %d want %d", j, g, w) } dSpans := mc.getSpans() // Expecting 0 spans to have been received by the original but now dead collector if g, w := len(dSpans), 0; g != w { t.Fatalf("Round #%d: Disconnected collector: spans: got %d want %d", j, g, w) } // Disconnect for the next try. require.NoError(t, nmc.stop()) } } // This test takes a long time to run: to skip it, run tests using: -short func TestNew_collectorOnBadConnection(t *testing.T) { if testing.Short() { t.Skipf("Skipping this long running test") } ln, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to grab an available port: %v", err) } // Firstly close the "collector's" channel: optimistically this endpoint won't get reused ASAP // However, our goal of closing it is to simulate an unavailable connection _ = ln.Close() _, collectorPortStr, _ := net.SplitHostPort(ln.Addr().String()) endpoint := fmt.Sprintf("localhost:%s", collectorPortStr) ctx := context.Background() exp := newGRPCExporter(t, ctx, endpoint) _ = exp.Shutdown(ctx) } func TestNew_withEndpoint(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) _ = exp.Shutdown(ctx) } func TestNew_withHeaders(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlptracegrpc.WithHeaders(map[string]string{"header1": "value1"})) require.NoError(t, exp.ExportSpans(ctx, roSpans)) defer func() { _ = exp.Shutdown(ctx) }() headers := mc.getHeaders() require.Len(t, headers.Get("header1"), 1) assert.Equal(t, "value1", headers.Get("header1")[0]) } func TestNew_WithTimeout(t *testing.T) { tts := []struct { name string fn func(exp *otlptrace.Exporter) error timeout time.Duration spans int code codes.Code delay bool }{ { name: "Timeout Spans", fn: func(exp *otlptrace.Exporter) error { return exp.ExportSpans(context.Background(), roSpans) }, timeout: time.Millisecond * 100, code: codes.DeadlineExceeded, delay: true, }, { name: "No Timeout Spans", fn: func(exp *otlptrace.Exporter) error { return exp.ExportSpans(context.Background(), roSpans) }, timeout: time.Minute, spans: 1, code: codes.OK, }, } for _, tt := range tts { t.Run(tt.name, func(t *testing.T) { mc := runMockCollector(t) if tt.delay { mc.traceSvc.delay = time.Second * 10 } defer func() { _ = mc.stop() }() ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint, otlptracegrpc.WithTimeout(tt.timeout), otlptracegrpc.WithRetry(otlptracegrpc.RetryConfig{Enabled: false})) defer func() { _ = exp.Shutdown(ctx) }() err := tt.fn(exp) if tt.code == codes.OK { require.NoError(t, err) } else { require.Error(t, err) } s := status.Convert(err) require.Equal(t, tt.code, s.Code()) require.Len(t, mc.getSpans(), tt.spans) }) } } func TestNew_withInvalidSecurityConfiguration(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() ctx := context.Background() driver := otlptracegrpc.NewClient(otlptracegrpc.WithEndpoint(mc.endpoint)) exp, err := otlptrace.New(ctx, driver) if err != nil { t.Fatalf("failed to create a new collector exporter: %v", err) } err = exp.ExportSpans(ctx, roSpans) expectedErr := fmt.Sprintf("traces exporter is disconnected from the server %s: grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)", mc.endpoint) require.Error(t, err) require.Equal(t, expectedErr, err.Error()) defer func() { _ = exp.Shutdown(ctx) }() } func TestNew_withMultipleAttributeTypes(t *testing.T) { mc := runMockCollector(t) defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { _ = exp.Shutdown(ctx) }() tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher( exp, // add following two options to ensure flush sdktrace.WithBatchTimeout(5*time.Second), sdktrace.WithMaxExportBatchSize(10), ), ) defer func() { _ = tp.Shutdown(ctx) }() tr := tp.Tracer("test-tracer") testKvs := []attribute.KeyValue{ attribute.Int("Int", 1), attribute.Int64("Int64", int64(3)), attribute.Float64("Float64", 2.22), attribute.Bool("Bool", true), attribute.String("String", "test"), } _, span := tr.Start(ctx, "AlwaysSample") span.SetAttributes(testKvs...) span.End() // Flush and close. func() { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := tp.Shutdown(ctx); err != nil { t.Fatalf("failed to shut down a tracer provider: %v", err) } }() // Wait >2 cycles. <-time.After(40 * time.Millisecond) // Now shutdown the exporter ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := exp.Shutdown(ctx); err != nil { t.Fatalf("failed to stop the exporter: %v", err) } // Shutdown the collector too so that we can begin // verification checks of expected data back. _ = mc.stop() // Now verify that we only got one span rss := mc.getSpans() if got, want := len(rss), 1; got != want { t.Fatalf("resource span count: got %d, want %d\n", got, want) } expected := []*commonpb.KeyValue{ { Key: "Int", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 1, }, }, }, { Key: "Int64", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_IntValue{ IntValue: 3, }, }, }, { Key: "Float64", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_DoubleValue{ DoubleValue: 2.22, }, }, }, { Key: "Bool", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_BoolValue{ BoolValue: true, }, }, }, { Key: "String", Value: &commonpb.AnyValue{ Value: &commonpb.AnyValue_StringValue{ StringValue: "test", }, }, }, } // Verify attributes if !assert.Len(t, rss[0].Attributes, len(expected)) { t.Fatalf("attributes count: got %d, want %d\n", len(rss[0].Attributes), len(expected)) } for i, actual := range rss[0].Attributes { if a, ok := actual.Value.Value.(*commonpb.AnyValue_DoubleValue); ok { e, ok := expected[i].Value.Value.(*commonpb.AnyValue_DoubleValue) if !ok { t.Errorf("expected AnyValue_DoubleValue, got %T", expected[i].Value.Value) continue } if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { continue } e.DoubleValue = a.DoubleValue } assert.Equal(t, expected[i], actual) } } func TestDisconnected(t *testing.T) { ctx := context.Background() // The endpoint is whatever, we want to be disconnected. But we // setting a blocking connection, so dialing to the invalid // endpoint actually fails. exp := newGRPCExporter(t, ctx, "invalid", otlptracegrpc.WithReconnectionPeriod(time.Hour), otlptracegrpc.WithDialOption( grpc.WithBlock(), grpc.FailOnNonTempDialError(true), ), ) defer func() { assert.NoError(t, exp.Shutdown(ctx)) }() assert.Error(t, exp.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan())) } func TestEmptyData(t *testing.T) { mc := runMockCollectorAtEndpoint(t, "localhost:56561") defer func() { _ = mc.stop() }() <-time.After(5 * time.Millisecond) ctx := context.Background() exp := newGRPCExporter(t, ctx, mc.endpoint) defer func() { assert.NoError(t, exp.Shutdown(ctx)) }() assert.NoError(t, exp.ExportSpans(ctx, nil)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/exporter.go000066400000000000000000000021431414226744000306750ustar00rootroot00000000000000// 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 otlptracegrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" import ( "context" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" ) // New constructs a new Exporter and starts it. func New(ctx context.Context, opts ...Option) (*otlptrace.Exporter, error) { return otlptrace.New(ctx, NewClient(opts...)) } // NewUnstarted constructs a new Exporter and does not start it. func NewUnstarted(opts ...Option) *otlptrace.Exporter { return otlptrace.NewUnstarted(NewClient(opts...)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/go.mod000066400000000000000000000055531414226744000276140ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go 1.15 require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/grpc v1.41.0 ) replace go.opentelemetry.io/otel => ../../../.. replace go.opentelemetry.io/otel/sdk => ../../../../sdk replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../ replace go.opentelemetry.io/otel/metric => ../../../../metric replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ./ replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/go.sum000066400000000000000000000314251414226744000276360ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/mock_collector_test.go000066400000000000000000000135011414226744000330630ustar00rootroot00000000000000// 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 otlptracegrpc_test import ( "context" "fmt" "net" "runtime" "strings" "sync" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlptracetest" "google.golang.org/grpc" "google.golang.org/grpc/metadata" collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) func makeMockCollector(t *testing.T, mockConfig *mockConfig) *mockCollector { return &mockCollector{ t: t, traceSvc: &mockTraceService{ storage: otlptracetest.NewSpansStorage(), errors: mockConfig.errors, }, } } type mockTraceService struct { collectortracepb.UnimplementedTraceServiceServer errors []error requests int mu sync.RWMutex storage otlptracetest.SpansStorage headers metadata.MD delay time.Duration } func (mts *mockTraceService) getHeaders() metadata.MD { mts.mu.RLock() defer mts.mu.RUnlock() return mts.headers } func (mts *mockTraceService) getSpans() []*tracepb.Span { mts.mu.RLock() defer mts.mu.RUnlock() return mts.storage.GetSpans() } func (mts *mockTraceService) getResourceSpans() []*tracepb.ResourceSpans { mts.mu.RLock() defer mts.mu.RUnlock() return mts.storage.GetResourceSpans() } func (mts *mockTraceService) Export(ctx context.Context, exp *collectortracepb.ExportTraceServiceRequest) (*collectortracepb.ExportTraceServiceResponse, error) { if mts.delay > 0 { time.Sleep(mts.delay) } mts.mu.Lock() defer func() { mts.requests++ mts.mu.Unlock() }() reply := &collectortracepb.ExportTraceServiceResponse{} if mts.requests < len(mts.errors) { idx := mts.requests return reply, mts.errors[idx] } mts.headers, _ = metadata.FromIncomingContext(ctx) mts.storage.AddSpans(exp) return reply, nil } type mockCollector struct { t *testing.T traceSvc *mockTraceService endpoint string ln *listener stopFunc func() stopOnce sync.Once } type mockConfig struct { errors []error endpoint string } var _ collectortracepb.TraceServiceServer = (*mockTraceService)(nil) var errAlreadyStopped = fmt.Errorf("already stopped") func (mc *mockCollector) stop() error { var err = errAlreadyStopped mc.stopOnce.Do(func() { err = nil if mc.stopFunc != nil { mc.stopFunc() } }) // Give it sometime to shutdown. <-time.After(160 * time.Millisecond) // Getting the lock ensures the traceSvc is done flushing. mc.traceSvc.mu.Lock() defer mc.traceSvc.mu.Unlock() return err } func (mc *mockCollector) Stop() error { return mc.stop() } func (mc *mockCollector) getSpans() []*tracepb.Span { return mc.traceSvc.getSpans() } func (mc *mockCollector) getResourceSpans() []*tracepb.ResourceSpans { return mc.traceSvc.getResourceSpans() } func (mc *mockCollector) GetResourceSpans() []*tracepb.ResourceSpans { return mc.getResourceSpans() } func (mc *mockCollector) getHeaders() metadata.MD { return mc.traceSvc.getHeaders() } // runMockCollector is a helper function to create a mock Collector func runMockCollector(t *testing.T) *mockCollector { return runMockCollectorAtEndpoint(t, "localhost:0") } func runMockCollectorAtEndpoint(t *testing.T, endpoint string) *mockCollector { return runMockCollectorWithConfig(t, &mockConfig{endpoint: endpoint}) } func runMockCollectorWithConfig(t *testing.T, mockConfig *mockConfig) *mockCollector { ln, err := net.Listen("tcp", mockConfig.endpoint) if err != nil { t.Fatalf("Failed to get an endpoint: %v", err) } srv := grpc.NewServer() mc := makeMockCollector(t, mockConfig) collectortracepb.RegisterTraceServiceServer(srv, mc.traceSvc) mc.ln = newListener(ln) go func() { _ = srv.Serve((net.Listener)(mc.ln)) }() mc.endpoint = ln.Addr().String() // srv.Stop calls Close on mc.ln. mc.stopFunc = srv.Stop return mc } type listener struct { closeOnce sync.Once wrapped net.Listener C chan struct{} } func newListener(wrapped net.Listener) *listener { return &listener{ wrapped: wrapped, C: make(chan struct{}, 1), } } func (l *listener) Close() error { return l.wrapped.Close() } func (l *listener) Addr() net.Addr { return l.wrapped.Addr() } // Accept waits for and returns the next connection to the listener. It will // send a signal on l.C that a connection has been made before returning. func (l *listener) Accept() (net.Conn, error) { conn, err := l.wrapped.Accept() if err != nil { // Go 1.16 exported net.ErrClosed that could clean up this check, but to // remain backwards compatible with previous versions of Go that we // support the following string evaluation is used instead to keep in line // with the previously recommended way to check this: // https://github.com/golang/go/issues/4373#issuecomment-353076799 if strings.Contains(err.Error(), "use of closed network connection") { // If the listener has been closed, do not allow callers of // WaitForConn to wait for a connection that will never come. l.closeOnce.Do(func() { close(l.C) }) } return conn, err } select { case l.C <- struct{}{}: default: // If C is full, assume nobody is listening and move on. } return conn, nil } // WaitForConn will wait indefintely for a connection to be estabilished with // the listener before returning. func (l *listener) WaitForConn() { for { select { case <-l.C: return default: runtime.Gosched() } } } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracegrpc/options.go000066400000000000000000000127011414226744000305210ustar00rootroot00000000000000// 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 otlptracegrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" import ( "fmt" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) // Option applies an option to the gRPC driver. type Option interface { applyGRPCOption(*otlpconfig.Config) } // RetryConfig defines configuration for retrying batches in case of export // failure using an exponential backoff. type RetryConfig retry.Config type wrappedOption struct { otlpconfig.GRPCOption } func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) { 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. Note, by default, client security is required unless WithInsecure is used. func WithInsecure() Option { return wrappedOption{otlpconfig.WithInsecure()} } // WithEndpoint allows one to set the endpoint that the exporter will // connect to the collector on. If unset, it will instead try to use // connect to DefaultCollectorHost:DefaultCollectorPort. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } // WithReconnectionPeriod allows one to set the delay between next connection attempt // after failing to connect with the collector. func WithReconnectionPeriod(rp time.Duration) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.ReconnectionPeriod = rp })} } func compressorToCompression(compressor string) otlpconfig.Compression { switch compressor { case "gzip": return otlpconfig.GzipCompression } otel.Handle(fmt.Errorf("invalid compression type: '%s', using no compression as default", compressor)) return otlpconfig.NoCompression } // WithCompressor will set the compressor for the gRPC client to use when sending requests. // It is the responsibility of the caller to ensure that the compressor set has been registered // with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some // compressors auto-register on import, such as gzip, which can be registered by calling // `import _ "google.golang.org/grpc/encoding/gzip"`. func WithCompressor(compressor string) Option { return wrappedOption{otlpconfig.WithCompression(compressorToCompression(compressor))} } // WithHeaders will send the provided headers with gRPC requests. func WithHeaders(headers map[string]string) Option { return wrappedOption{otlpconfig.WithHeaders(headers)} } // WithTLSCredentials allows the connection to use TLS credentials // when talking to the server. It takes in grpc.TransportCredentials instead // of say a Certificate file or a tls.Certificate, because the retrieving of // these credentials can be done in many ways e.g. plain file, in code tls.Config // or by certificate rotation, so it is up to the caller to decide what to use. func WithTLSCredentials(creds credentials.TransportCredentials) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.Traces.GRPCCredentials = creds })} } // WithServiceConfig defines the default gRPC service config used. func WithServiceConfig(serviceConfig string) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.ServiceConfig = serviceConfig })} } // WithDialOption opens support to any grpc.DialOption to be used. If it conflicts // with some other configuration the GRPC specified via the collector the ones here will // take preference since they are set last. func WithDialOption(opts ...grpc.DialOption) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.DialOptions = opts })} } // WithGRPCConn allows reusing existing gRPC connection when it has already been // established for other services. When set, other dial options will be ignored. func WithGRPCConn(conn *grpc.ClientConn) Option { return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { cfg.GRPCConn = conn })} } // WithTimeout tells the driver the max waiting time for the backend to process // each spans batch. If unset, the default will be 10 seconds. func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } // WithRetry configures the retry policy for transient errors that may occurs // when exporting traces. An exponential back-off algorithm is used to ensure // endpoints are not overwhelmed with retries. If unset, the default retry // policy will retry after 5 seconds and increase exponentially after each // error for a total of 1 minute. func WithRetry(settings RetryConfig) Option { return wrappedOption{otlpconfig.WithRetry(retry.Config(settings))} } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/000077500000000000000000000000001414226744000265225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/certificate_test.go000066400000000000000000000051631414226744000323770ustar00rootroot00000000000000// 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 otlptracehttp_test import ( "bytes" "crypto/ecdsa" "crypto/elliptic" cryptorand "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" mathrand "math/rand" "net" "time" ) type mathRandReader struct{} func (mathRandReader) Read(p []byte) (n int, err error) { return mathrand.Read(p) } var randReader mathRandReader type pemCertificate struct { Certificate []byte PrivateKey []byte } // Based on https://golang.org/src/crypto/tls/generate_cert.go, // simplified and weakened. func generateWeakCertificate() (*pemCertificate, error) { priv, err := ecdsa.GenerateKey(elliptic.P256(), randReader) if err != nil { return nil, err } keyUsage := x509.KeyUsageDigitalSignature notBefore := time.Now() notAfter := notBefore.Add(time.Hour) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := cryptorand.Int(randReader, serialNumberLimit) if err != nil { return nil, err } template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"otel-go"}, }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: keyUsage, 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, &template, &template, &priv.PublicKey, priv) if err != nil { return nil, err } certificateBuffer := new(bytes.Buffer) if err := pem.Encode(certificateBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { return nil, err } privDERBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { return nil, err } privBuffer := new(bytes.Buffer) if err := pem.Encode(privBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: privDERBytes}); err != nil { return nil, err } return &pemCertificate{ Certificate: certificateBuffer.Bytes(), PrivateKey: privBuffer.Bytes(), }, nil } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/client.go000066400000000000000000000173341414226744000303370ustar00rootroot00000000000000// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" import ( "bytes" "compress/gzip" "context" "fmt" "io" "io/ioutil" "net" "net/http" "path" "strconv" "strings" "sync" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" "google.golang.org/protobuf/proto" coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" ) const contentTypeProto = "application/x-protobuf" var gzPool = sync.Pool{ New: func() interface{} { w := gzip.NewWriter(ioutil.Discard) return w }, } // Keep it in sync with golang's DefaultTransport from net/http! We // have our own copy to avoid handling a situation where the // DefaultTransport is overwritten with some different implementation // of http.RoundTripper or it's modified by other package. var ourTransport = &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } type client struct { name string cfg otlpconfig.SignalConfig generalCfg otlpconfig.Config requestFunc retry.RequestFunc client *http.Client stopCh chan struct{} } var _ otlptrace.Client = (*client)(nil) // NewClient creates a new HTTP trace client. func NewClient(opts ...Option) otlptrace.Client { cfg := otlpconfig.NewDefaultConfig() otlpconfig.ApplyHTTPEnvConfigs(&cfg) for _, opt := range opts { opt.applyHTTPOption(&cfg) } for pathPtr, defaultPath := range map[*string]string{ &cfg.Traces.URLPath: otlpconfig.DefaultTracesPath, } { tmp := strings.TrimSpace(*pathPtr) if tmp == "" { tmp = defaultPath } else { tmp = path.Clean(tmp) if !path.IsAbs(tmp) { tmp = fmt.Sprintf("/%s", tmp) } } *pathPtr = tmp } httpClient := &http.Client{ Transport: ourTransport, Timeout: cfg.Traces.Timeout, } if cfg.Traces.TLSCfg != nil { transport := ourTransport.Clone() transport.TLSClientConfig = cfg.Traces.TLSCfg httpClient.Transport = transport } stopCh := make(chan struct{}) return &client{ name: "traces", cfg: cfg.Traces, generalCfg: cfg, requestFunc: cfg.RetryConfig.RequestFunc(evaluate), stopCh: stopCh, client: httpClient, } } // Start does nothing in a HTTP client func (d *client) Start(ctx context.Context) error { // nothing to do select { case <-ctx.Done(): return ctx.Err() default: } return nil } // Stop shuts down the client and interrupt any in-flight request. func (d *client) Stop(ctx context.Context) error { close(d.stopCh) select { case <-ctx.Done(): return ctx.Err() default: } return nil } // UploadTraces sends a batch of spans to the collector. func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error { pbRequest := &coltracepb.ExportTraceServiceRequest{ ResourceSpans: protoSpans, } rawRequest, err := proto.Marshal(pbRequest) if err != nil { return err } ctx, cancel := d.contextWithStop(ctx) defer cancel() request, err := d.newRequest(rawRequest) if err != nil { return err } return d.requestFunc(ctx, func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: } request.reset(ctx) resp, err := d.client.Do(request.Request) if err != nil { return err } var rErr error switch resp.StatusCode { case http.StatusOK: // Success, do not retry. case http.StatusTooManyRequests, http.StatusServiceUnavailable: // Retry-able failure. rErr = newResponseError(resp.Header) // Going to retry, drain the body to reuse the connection. if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { _ = resp.Body.Close() return err } default: rErr = fmt.Errorf("failed to send %s to %s: %s", d.name, request.URL, resp.Status) } if err := resp.Body.Close(); err != nil { return err } return rErr }) } func (d *client) newRequest(body []byte) (request, error) { address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath) r, err := http.NewRequest(http.MethodPost, address, nil) if err != nil { return request{Request: r}, err } for k, v := range d.cfg.Headers { r.Header.Set(k, v) } r.Header.Set("Content-Type", contentTypeProto) req := request{Request: r} switch Compression(d.cfg.Compression) { case NoCompression: r.ContentLength = (int64)(len(body)) req.bodyReader = bodyReader(body) case GzipCompression: // Ensure the content length is not used. r.ContentLength = -1 r.Header.Set("Content-Encoding", "gzip") gz := gzPool.Get().(*gzip.Writer) defer gzPool.Put(gz) var b bytes.Buffer gz.Reset(&b) if _, err := gz.Write(body); err != nil { return req, err } // Close needs to be called to ensure body if fully written. if err := gz.Close(); err != nil { return req, err } req.bodyReader = bodyReader(b.Bytes()) } return req, nil } // bodyReader returns a closure returning a new reader for buf. func bodyReader(buf []byte) func() io.ReadCloser { return func() io.ReadCloser { return ioutil.NopCloser(bytes.NewReader(buf)) } } // request wraps an http.Request with a resettable body reader. type request struct { *http.Request // bodyReader allows the same body to be used for multiple requests. bodyReader func() io.ReadCloser } // reset reinitializes the request Body and uses ctx for the request. func (r *request) reset(ctx context.Context) { r.Body = r.bodyReader() r.Request = r.Request.WithContext(ctx) } // retryableError represents a request failure that can be retried. type retryableError struct { throttle int64 } // newResponseError returns a retryableError and will extract any explicit // throttle delay contained in headers. func newResponseError(header http.Header) error { var rErr retryableError if s, ok := header["Retry-After"]; ok { if t, err := strconv.ParseInt(s[0], 10, 64); err == nil { rErr.throttle = t } } return rErr } func (e retryableError) Error() string { return "retry-able request failure" } // evaluate returns if err is retry-able. If it is and it includes an explicit // throttling delay, that delay is also returned. func evaluate(err error) (bool, time.Duration) { if err == nil { return false, 0 } rErr, ok := err.(retryableError) if !ok { return false, 0 } return true, time.Duration(rErr.throttle) } func (d *client) getScheme() string { if d.cfg.Insecure { return "http" } return "https" } func (d *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { // Unify the parent context Done signal with the client's stop // channel. ctx, cancel := context.WithCancel(ctx) go func(ctx context.Context, cancel context.CancelFunc) { select { case <-ctx.Done(): // Nothing to do, either cancelled or deadline // happened. case <-d.stopCh: cancel() } }(ctx, cancel) return ctx, cancel } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/client_test.go000066400000000000000000000221571414226744000313750ustar00rootroot00000000000000// 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 otlptracehttp_test import ( "context" "fmt" "net/http" "os" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlptracetest" ) const ( relOtherTracesPath = "post/traces/here" otherTracesPath = "/post/traces/here" ) var ( testHeaders = map[string]string{ "Otel-Go-Key-1": "somevalue", "Otel-Go-Key-2": "someothervalue", } ) func TestEndToEnd(t *testing.T) { tests := []struct { name string opts []otlptracehttp.Option mcCfg mockCollectorConfig tls bool }{ { name: "no extra options", opts: nil, }, { name: "with gzip compression", opts: []otlptracehttp.Option{ otlptracehttp.WithCompression(otlptracehttp.GzipCompression), }, }, { name: "retry", opts: []otlptracehttp.Option{ otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: time.Nanosecond, MaxInterval: time.Nanosecond, // Do not stop trying. MaxElapsedTime: 0, }), }, mcCfg: mockCollectorConfig{ InjectHTTPStatus: []int{503, 429}, }, }, { name: "retry with gzip compression", opts: []otlptracehttp.Option{ otlptracehttp.WithCompression(otlptracehttp.GzipCompression), otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: time.Nanosecond, MaxInterval: time.Nanosecond, // Do not stop trying. MaxElapsedTime: 0, }), }, mcCfg: mockCollectorConfig{ InjectHTTPStatus: []int{503, 503}, }, }, { name: "retry with throttle", opts: []otlptracehttp.Option{ otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: time.Nanosecond, MaxInterval: time.Nanosecond, // Do not stop trying. MaxElapsedTime: 0, }), }, mcCfg: mockCollectorConfig{ InjectHTTPStatus: []int{503}, InjectResponseHeader: []map[string]string{ {"Retry-After": "10"}, }, }, }, { name: "with empty paths (forced to defaults)", opts: []otlptracehttp.Option{ otlptracehttp.WithURLPath(""), }, }, { name: "with relative paths", opts: []otlptracehttp.Option{ otlptracehttp.WithURLPath(relOtherTracesPath), }, mcCfg: mockCollectorConfig{ TracesURLPath: otherTracesPath, }, }, { name: "with TLS", opts: nil, mcCfg: mockCollectorConfig{ WithTLS: true, }, tls: true, }, { name: "with extra headers", opts: []otlptracehttp.Option{ otlptracehttp.WithHeaders(testHeaders), }, mcCfg: mockCollectorConfig{ ExpectedHeaders: testHeaders, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { mc := runMockCollector(t, tc.mcCfg) defer mc.MustStop(t) allOpts := []otlptracehttp.Option{ otlptracehttp.WithEndpoint(mc.Endpoint()), } if tc.tls { tlsConfig := mc.ClientTLSConfig() require.NotNil(t, tlsConfig) allOpts = append(allOpts, otlptracehttp.WithTLSClientConfig(tlsConfig)) } else { allOpts = append(allOpts, otlptracehttp.WithInsecure()) } allOpts = append(allOpts, tc.opts...) client := otlptracehttp.NewClient(allOpts...) ctx := context.Background() exporter, err := otlptrace.New(ctx, client) if assert.NoError(t, err) { defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() otlptracetest.RunEndToEndTest(ctx, t, exporter, mc) } }) } } func TestExporterShutdown(t *testing.T) { mc := runMockCollector(t, mockCollectorConfig{}) defer func() { _ = mc.Stop() }() <-time.After(5 * time.Millisecond) otlptracetest.RunExporterShutdownTest(t, func() otlptrace.Client { return otlptracehttp.NewClient( otlptracehttp.WithInsecure(), otlptracehttp.WithEndpoint(mc.endpoint), ) }) } func TestTimeout(t *testing.T) { mcCfg := mockCollectorConfig{ InjectDelay: 100 * time.Millisecond, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) client := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), otlptracehttp.WithTimeout(50*time.Millisecond), ) ctx := context.Background() exporter, err := otlptrace.New(ctx, client) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan()) assert.Equal(t, true, os.IsTimeout(err)) } func TestNoRetry(t *testing.T) { mc := runMockCollector(t, mockCollectorConfig{ InjectHTTPStatus: []int{http.StatusBadRequest}, }) defer mc.MustStop(t) driver := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: 1 * time.Nanosecond, MaxInterval: 1 * time.Nanosecond, // Never stop retry of retry-able status. MaxElapsedTime: 0, }), ) ctx := context.Background() exporter, err := otlptrace.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() err = exporter.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan()) assert.Error(t, err) assert.Equal(t, fmt.Sprintf("failed to send traces to http://%s/v1/traces: 400 Bad Request", mc.endpoint), err.Error()) assert.Empty(t, mc.GetSpans()) } func TestEmptyData(t *testing.T) { mcCfg := mockCollectorConfig{} mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), ) ctx := context.Background() exporter, err := otlptrace.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() assert.NoError(t, err) err = exporter.ExportSpans(ctx, nil) assert.NoError(t, err) assert.Empty(t, mc.GetSpans()) } func TestCancelledContext(t *testing.T) { mcCfg := mockCollectorConfig{} mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), ) ctx, cancel := context.WithCancel(context.Background()) exporter, err := otlptrace.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(context.Background())) }() cancel() err = exporter.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan()) assert.Error(t, err) assert.Empty(t, mc.GetSpans()) } func TestDeadlineContext(t *testing.T) { statuses := make([]int, 0, 5) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: 1 * time.Hour, MaxInterval: 1 * time.Hour, // Never stop retry of retry-able status. MaxElapsedTime: 0, }), ) ctx := context.Background() exporter, err := otlptrace.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(context.Background())) }() ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() err = exporter.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan()) assert.Error(t, err) assert.Empty(t, mc.GetSpans()) } func TestStopWhileExporting(t *testing.T) { statuses := make([]int, 0, 5) for i := 0; i < cap(statuses); i++ { statuses = append(statuses, http.StatusTooManyRequests) } mcCfg := mockCollectorConfig{ InjectHTTPStatus: statuses, } mc := runMockCollector(t, mcCfg) defer mc.MustStop(t) driver := otlptracehttp.NewClient( otlptracehttp.WithEndpoint(mc.Endpoint()), otlptracehttp.WithInsecure(), otlptracehttp.WithRetry(otlptracehttp.RetryConfig{ Enabled: true, InitialInterval: 1 * time.Hour, MaxInterval: 1 * time.Hour, // Never stop retry of retry-able status. MaxElapsedTime: 0, }), ) ctx := context.Background() exporter, err := otlptrace.New(ctx, driver) require.NoError(t, err) defer func() { assert.NoError(t, exporter.Shutdown(ctx)) }() doneCh := make(chan struct{}) go func() { err := exporter.ExportSpans(ctx, otlptracetest.SingleReadOnlySpan()) assert.Error(t, err) assert.Empty(t, mc.GetSpans()) close(doneCh) }() <-time.After(time.Second) err = exporter.Shutdown(ctx) assert.NoError(t, err) <-doneCh } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/doc.go000066400000000000000000000014521414226744000276200ustar00rootroot00000000000000// 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 otlptracehttp a client that sends traces to the collector using HTTP with binary protobuf payloads. */ package otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/example_test.go000066400000000000000000000047141414226744000315510ustar00rootroot00000000000000// 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 otlptracehttp_test import ( "context" "log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) const ( instrumentationName = "github.com/instrumentron" instrumentationVersion = "v0.1.0" ) var ( tracer = otel.GetTracerProvider().Tracer( instrumentationName, trace.WithInstrumentationVersion(instrumentationVersion), trace.WithSchemaURL(semconv.SchemaURL), ) ) func add(ctx context.Context, x, y int64) int64 { var span trace.Span _, span = tracer.Start(ctx, "Addition") defer span.End() return x + y } func multiply(ctx context.Context, x, y int64) int64 { var span trace.Span _, span = tracer.Start(ctx, "Multiplication") defer span.End() return x * y } func newResource() *resource.Resource { return resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("otlptrace-example"), semconv.ServiceVersionKey.String("0.0.1"), ) } func installExportPipeline(ctx context.Context) func() { client := otlptracehttp.NewClient() exporter, err := otlptrace.New(ctx, client) if err != nil { log.Fatalf("creating OTLP trace exporter: %v", err) } tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(newResource()), ) otel.SetTracerProvider(tracerProvider) return func() { if err := tracerProvider.Shutdown(ctx); err != nil { log.Fatalf("stopping tracer provider: %v", err) } } } func Example() { ctx := context.Background() // Registers a tracer Provider globally. cleanup := installExportPipeline(ctx) defer cleanup() log.Println("the answer is", add(ctx, multiply(ctx, multiply(ctx, 2, 2), 10), 2)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/exporter.go000066400000000000000000000021431414226744000307210ustar00rootroot00000000000000// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" import ( "context" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" ) // New constructs a new Exporter and starts it. func New(ctx context.Context, opts ...Option) (*otlptrace.Exporter, error) { return otlptrace.New(ctx, NewClient(opts...)) } // NewUnstarted constructs a new Exporter and does not start it. func NewUnstarted(opts ...Option) *otlptrace.Exporter { return otlptrace.NewUnstarted(NewClient(opts...)) } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/go.mod000066400000000000000000000056261414226744000276410ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp go 1.15 require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 go.opentelemetry.io/proto/otlp v0.9.0 google.golang.org/protobuf v1.27.1 ) replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../ replace go.opentelemetry.io/otel => ../../../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ./ replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools replace go.opentelemetry.io/otel/metric => ../../../../metric replace go.opentelemetry.io/otel/sdk => ../../../../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../../schema golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/go.sum000066400000000000000000000314251414226744000276620ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/mock_collector_test.go000066400000000000000000000152241414226744000331130ustar00rootroot00000000000000// 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 otlptracehttp_test import ( "bytes" "compress/gzip" "context" "crypto/tls" "fmt" "io" "io/ioutil" "net" "net/http" "sync" "testing" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlptracetest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) type mockCollector struct { endpoint string server *http.Server spanLock sync.Mutex spansStorage otlptracetest.SpansStorage injectHTTPStatus []int injectResponseHeader []map[string]string injectContentType string injectDelay time.Duration clientTLSConfig *tls.Config expectedHeaders map[string]string } func (c *mockCollector) Stop() error { return c.server.Shutdown(context.Background()) } func (c *mockCollector) MustStop(t *testing.T) { assert.NoError(t, c.server.Shutdown(context.Background())) } func (c *mockCollector) GetSpans() []*tracepb.Span { c.spanLock.Lock() defer c.spanLock.Unlock() return c.spansStorage.GetSpans() } func (c *mockCollector) GetResourceSpans() []*tracepb.ResourceSpans { c.spanLock.Lock() defer c.spanLock.Unlock() return c.spansStorage.GetResourceSpans() } func (c *mockCollector) Endpoint() string { return c.endpoint } func (c *mockCollector) ClientTLSConfig() *tls.Config { return c.clientTLSConfig } func (c *mockCollector) serveTraces(w http.ResponseWriter, r *http.Request) { if c.injectDelay != 0 { time.Sleep(c.injectDelay) } if !c.checkHeaders(r) { w.WriteHeader(http.StatusBadRequest) return } response := collectortracepb.ExportTraceServiceResponse{} rawResponse, err := proto.Marshal(&response) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } h := c.getInjectResponseHeader() if injectedStatus := c.getInjectHTTPStatus(); injectedStatus != 0 { writeReply(w, rawResponse, injectedStatus, c.injectContentType, h) return } rawRequest, err := readRequest(r) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } request, err := unmarshalTraceRequest(rawRequest, r.Header.Get("content-type")) if err != nil { w.WriteHeader(http.StatusBadRequest) return } writeReply(w, rawResponse, 0, c.injectContentType, h) c.spanLock.Lock() defer c.spanLock.Unlock() c.spansStorage.AddSpans(request) } func unmarshalTraceRequest(rawRequest []byte, contentType string) (*collectortracepb.ExportTraceServiceRequest, error) { request := &collectortracepb.ExportTraceServiceRequest{} if contentType != "application/x-protobuf" { return request, fmt.Errorf("invalid content-type: %s, only application/x-protobuf is supported", contentType) } err := proto.Unmarshal(rawRequest, request) return request, err } func (c *mockCollector) checkHeaders(r *http.Request) bool { for k, v := range c.expectedHeaders { got := r.Header.Get(k) if got != v { return false } } return true } func (c *mockCollector) getInjectHTTPStatus() int { if len(c.injectHTTPStatus) == 0 { return 0 } status := c.injectHTTPStatus[0] c.injectHTTPStatus = c.injectHTTPStatus[1:] if len(c.injectHTTPStatus) == 0 { c.injectHTTPStatus = nil } return status } func (c *mockCollector) getInjectResponseHeader() (h map[string]string) { if len(c.injectResponseHeader) == 0 { return } h, c.injectResponseHeader = c.injectResponseHeader[0], c.injectResponseHeader[1:] if len(c.injectResponseHeader) == 0 { c.injectResponseHeader = nil } return } func readRequest(r *http.Request) ([]byte, error) { if r.Header.Get("Content-Encoding") == "gzip" { return readGzipBody(r.Body) } return ioutil.ReadAll(r.Body) } func readGzipBody(body io.Reader) ([]byte, error) { rawRequest := bytes.Buffer{} gunzipper, err := gzip.NewReader(body) if err != nil { return nil, err } defer gunzipper.Close() _, err = io.Copy(&rawRequest, gunzipper) if err != nil { return nil, err } return rawRequest.Bytes(), nil } func writeReply(w http.ResponseWriter, rawResponse []byte, s int, ct string, h map[string]string) { status := http.StatusOK if s != 0 { status = s } contentType := "application/x-protobuf" if ct != "" { contentType = ct } w.Header().Set("Content-Type", contentType) for k, v := range h { w.Header().Add(k, v) } w.WriteHeader(status) _, _ = w.Write(rawResponse) } type mockCollectorConfig struct { TracesURLPath string Port int InjectHTTPStatus []int InjectContentType string InjectResponseHeader []map[string]string InjectDelay time.Duration WithTLS bool ExpectedHeaders map[string]string } func (c *mockCollectorConfig) fillInDefaults() { if c.TracesURLPath == "" { c.TracesURLPath = otlpconfig.DefaultTracesPath } } func runMockCollector(t *testing.T, cfg mockCollectorConfig) *mockCollector { cfg.fillInDefaults() ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", cfg.Port)) require.NoError(t, err) _, portStr, err := net.SplitHostPort(ln.Addr().String()) require.NoError(t, err) m := &mockCollector{ endpoint: fmt.Sprintf("localhost:%s", portStr), spansStorage: otlptracetest.NewSpansStorage(), injectHTTPStatus: cfg.InjectHTTPStatus, injectResponseHeader: cfg.InjectResponseHeader, injectContentType: cfg.InjectContentType, injectDelay: cfg.InjectDelay, expectedHeaders: cfg.ExpectedHeaders, } mux := http.NewServeMux() mux.Handle(cfg.TracesURLPath, http.HandlerFunc(m.serveTraces)) server := &http.Server{ Handler: mux, } if cfg.WithTLS { pem, err := generateWeakCertificate() require.NoError(t, err) tlsCertificate, err := tls.X509KeyPair(pem.Certificate, pem.PrivateKey) require.NoError(t, err) server.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{tlsCertificate}, } m.clientTLSConfig = &tls.Config{ InsecureSkipVerify: true, } } go func() { if cfg.WithTLS { _ = server.ServeTLS(ln, "", "") } else { _ = server.Serve(ln) } }() m.server = server return m } golang-opentelemetry-otel-1.1.0/exporters/otlp/otlptrace/otlptracehttp/options.go000066400000000000000000000076401414226744000305530ustar00rootroot00000000000000// 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 otlptracehttp // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" import ( "crypto/tls" "time" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry" ) // Compression describes the compression used for payloads sent to the // collector. type Compression otlpconfig.Compression const ( // NoCompression tells the driver to send payloads without // compression. NoCompression = Compression(otlpconfig.NoCompression) // GzipCompression tells the driver to send payloads after // compressing them with gzip. GzipCompression = Compression(otlpconfig.GzipCompression) ) // Option applies an option to the HTTP client. type Option interface { applyHTTPOption(*otlpconfig.Config) } // RetryConfig defines configuration for retrying batches in case of export // failure using an exponential backoff. type RetryConfig retry.Config type wrappedOption struct { otlpconfig.HTTPOption } func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) { w.ApplyHTTPOption(cfg) } // WithEndpoint allows one to set the address of the collector // endpoint that the driver will use to send spans. If // unset, it will instead try to use // the default endpoint (localhost:4317). Note that the endpoint // must not contain any URL path. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } // WithCompression tells the driver to compress the sent data. func WithCompression(compression Compression) Option { return wrappedOption{otlpconfig.WithCompression(otlpconfig.Compression(compression))} } // WithURLPath allows one to override the default URL path used // for sending traces. If unset, default ("/v1/traces") will be used. func WithURLPath(urlPath string) Option { return wrappedOption{otlpconfig.WithURLPath(urlPath)} } // WithTLSClientConfig can be used to set up a custom TLS // configuration for the client used to send payloads to the // collector. Use it if you want to use a custom certificate. func WithTLSClientConfig(tlsCfg *tls.Config) Option { return wrappedOption{otlpconfig.WithTLSClientConfig(tlsCfg)} } // WithInsecure tells the driver to connect to the collector using the // HTTP scheme, instead of HTTPS. func WithInsecure() Option { return wrappedOption{otlpconfig.WithInsecure()} } // WithHeaders allows one to tell the driver to send additional HTTP // headers with the payloads. Specifying headers like Content-Length, // Content-Encoding and Content-Type may result in a broken driver. func WithHeaders(headers map[string]string) Option { return wrappedOption{otlpconfig.WithHeaders(headers)} } // WithTimeout tells the driver the max waiting time for the backend to process // each spans batch. If unset, the default will be 10 seconds. func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } // WithRetry configures the retry policy for transient errors that may occurs // when exporting traces. An exponential back-off algorithm is used to ensure // endpoints are not overwhelmed with retries. If unset, the default retry // policy will retry after 5 seconds and increase exponentially after each // error for a total of 1 minute. func WithRetry(rc RetryConfig) Option { return wrappedOption{otlpconfig.WithRetry(retry.Config(rc))} } golang-opentelemetry-otel-1.1.0/exporters/prometheus/000077500000000000000000000000001414226744000230455ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/prometheus/README.md000066400000000000000000000002341414226744000243230ustar00rootroot00000000000000# OpenTelemetry-Go Prometheus Exporter OpenTelemetry Prometheus exporter ## Installation ``` go get -u go.opentelemetry.io/otel/exporters/prometheus ``` golang-opentelemetry-otel-1.1.0/exporters/prometheus/go.mod000066400000000000000000000055441414226744000241630ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/prometheus go 1.15 require ( github.com/prometheus/client_golang v1.11.0 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 ) replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/otlp => ../otlp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/prometheus => ./ replace go.opentelemetry.io/otel/exporters/jaeger => ../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../zipkin replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/exporters/prometheus/go.sum000066400000000000000000000345351414226744000242120ustar00rootroot00000000000000cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/exporters/prometheus/prometheus.go000066400000000000000000000247461414226744000256040ustar00rootroot00000000000000// 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 prometheus // import "go.opentelemetry.io/otel/exporters/prometheus" // Note that this package does not support a way to export Prometheus // Summary data points, removed in PR#1412. import ( "context" "fmt" "net/http" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/resource" ) // Exporter supports Prometheus pulls. It does not implement the // sdk/export/metric.Exporter interface--instead it creates a pull // controller and reads the latest checkpointed data on-scrape. type Exporter struct { handler http.Handler registerer prometheus.Registerer gatherer prometheus.Gatherer // lock protects access to the controller. The controller // exposes its own lock, but using a dedicated lock in this // struct allows the exporter to potentially support multiple // controllers (e.g., with different resources). lock sync.RWMutex controller *controller.Controller defaultHistogramBoundaries []float64 } // ErrUnsupportedAggregator is returned for unrepresentable aggregator // types (e.g., exact). var ErrUnsupportedAggregator = fmt.Errorf("unsupported aggregator type") var _ http.Handler = &Exporter{} // Config is a set of configs for the tally reporter. type Config struct { // Registry is the prometheus registry that will be used as the default Registerer and // Gatherer if these are not specified. // // If not set a new empty Registry is created. Registry *prometheus.Registry // Registerer is the prometheus registerer to register // metrics with. // // If not specified the Registry will be used as default. Registerer prometheus.Registerer // Gatherer is the prometheus gatherer to gather // metrics with. // // If not specified the Registry will be used as default. Gatherer prometheus.Gatherer // DefaultHistogramBoundaries defines the default histogram bucket // boundaries. DefaultHistogramBoundaries []float64 } // New returns a new Prometheus exporter using the configured metric // controller. See controller.New(). func New(config Config, controller *controller.Controller) (*Exporter, error) { if config.Registry == nil { config.Registry = prometheus.NewRegistry() } if config.Registerer == nil { config.Registerer = config.Registry } if config.Gatherer == nil { config.Gatherer = config.Registry } e := &Exporter{ handler: promhttp.HandlerFor(config.Gatherer, promhttp.HandlerOpts{}), registerer: config.Registerer, gatherer: config.Gatherer, controller: controller, defaultHistogramBoundaries: config.DefaultHistogramBoundaries, } c := &collector{ exp: e, } if err := config.Registerer.Register(c); err != nil { return nil, fmt.Errorf("cannot register the collector: %w", err) } return e, nil } // MeterProvider returns the MeterProvider of this exporter. func (e *Exporter) MeterProvider() metric.MeterProvider { return e.controller } // Controller returns the controller object that coordinates collection for the SDK. func (e *Exporter) Controller() *controller.Controller { e.lock.RLock() defer e.lock.RUnlock() return e.controller } // TemporalityFor implements TemporalitySelector. func (e *Exporter) TemporalityFor(desc *sdkapi.Descriptor, kind aggregation.Kind) aggregation.Temporality { return aggregation.CumulativeTemporalitySelector().TemporalityFor(desc, kind) } // ServeHTTP implements http.Handler. func (e *Exporter) ServeHTTP(w http.ResponseWriter, r *http.Request) { e.handler.ServeHTTP(w, r) } // collector implements prometheus.Collector interface. type collector struct { exp *Exporter } var _ prometheus.Collector = (*collector)(nil) // Describe implements prometheus.Collector. func (c *collector) Describe(ch chan<- *prometheus.Desc) { c.exp.lock.RLock() defer c.exp.lock.RUnlock() _ = c.exp.Controller().ForEach(func(_ instrumentation.Library, reader export.Reader) error { return reader.ForEach(c.exp, func(record export.Record) error { var labelKeys []string mergeLabels(record, c.exp.controller.Resource(), &labelKeys, nil) ch <- c.toDesc(record, labelKeys) return nil }) }) } // Collect exports the last calculated Reader state. // // Collect is invoked whenever prometheus.Gatherer is also invoked. // For example, when the HTTP endpoint is invoked by Prometheus. func (c *collector) Collect(ch chan<- prometheus.Metric) { c.exp.lock.RLock() defer c.exp.lock.RUnlock() ctrl := c.exp.Controller() if err := ctrl.Collect(context.Background()); err != nil { otel.Handle(err) } err := ctrl.ForEach(func(_ instrumentation.Library, reader export.Reader) error { return reader.ForEach(c.exp, func(record export.Record) error { agg := record.Aggregation() numberKind := record.Descriptor().NumberKind() instrumentKind := record.Descriptor().InstrumentKind() var labelKeys, labels []string mergeLabels(record, c.exp.controller.Resource(), &labelKeys, &labels) desc := c.toDesc(record, labelKeys) if hist, ok := agg.(aggregation.Histogram); ok { if err := c.exportHistogram(ch, hist, numberKind, desc, labels); err != nil { return fmt.Errorf("exporting histogram: %w", err) } } else if sum, ok := agg.(aggregation.Sum); ok && instrumentKind.Monotonic() { if err := c.exportMonotonicCounter(ch, sum, numberKind, desc, labels); err != nil { return fmt.Errorf("exporting monotonic counter: %w", err) } } else if sum, ok := agg.(aggregation.Sum); ok && !instrumentKind.Monotonic() { if err := c.exportNonMonotonicCounter(ch, sum, numberKind, desc, labels); err != nil { return fmt.Errorf("exporting non monotonic counter: %w", err) } } else if lastValue, ok := agg.(aggregation.LastValue); ok { if err := c.exportLastValue(ch, lastValue, numberKind, desc, labels); err != nil { return fmt.Errorf("exporting last value: %w", err) } } else { return fmt.Errorf("%w: %s", ErrUnsupportedAggregator, agg.Kind()) } return nil }) }) if err != nil { otel.Handle(err) } } func (c *collector) exportLastValue(ch chan<- prometheus.Metric, lvagg aggregation.LastValue, kind number.Kind, desc *prometheus.Desc, labels []string) error { lv, _, err := lvagg.LastValue() if err != nil { return fmt.Errorf("error retrieving last value: %w", err) } m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, lv.CoerceToFloat64(kind), labels...) if err != nil { return fmt.Errorf("error creating constant metric: %w", err) } ch <- m return nil } func (c *collector) exportNonMonotonicCounter(ch chan<- prometheus.Metric, sum aggregation.Sum, kind number.Kind, desc *prometheus.Desc, labels []string) error { v, err := sum.Sum() if err != nil { return fmt.Errorf("error retrieving counter: %w", err) } m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, v.CoerceToFloat64(kind), labels...) if err != nil { return fmt.Errorf("error creating constant metric: %w", err) } ch <- m return nil } func (c *collector) exportMonotonicCounter(ch chan<- prometheus.Metric, sum aggregation.Sum, kind number.Kind, desc *prometheus.Desc, labels []string) error { v, err := sum.Sum() if err != nil { return fmt.Errorf("error retrieving counter: %w", err) } m, err := prometheus.NewConstMetric(desc, prometheus.CounterValue, v.CoerceToFloat64(kind), labels...) if err != nil { return fmt.Errorf("error creating constant metric: %w", err) } ch <- m return nil } func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregation.Histogram, kind number.Kind, desc *prometheus.Desc, labels []string) error { buckets, err := hist.Histogram() if err != nil { return fmt.Errorf("error retrieving histogram: %w", err) } sum, err := hist.Sum() if err != nil { return fmt.Errorf("error retrieving sum: %w", err) } var totalCount uint64 // counts maps from the bucket upper-bound to the cumulative count. // The bucket with upper-bound +inf is not included. counts := make(map[float64]uint64, len(buckets.Boundaries)) for i := range buckets.Boundaries { boundary := buckets.Boundaries[i] totalCount += uint64(buckets.Counts[i]) counts[boundary] = totalCount } // Include the +inf bucket in the total count. totalCount += uint64(buckets.Counts[len(buckets.Counts)-1]) m, err := prometheus.NewConstHistogram(desc, totalCount, sum.CoerceToFloat64(kind), counts, labels...) if err != nil { return fmt.Errorf("error creating constant histogram: %w", err) } ch <- m return nil } func (c *collector) toDesc(record export.Record, labelKeys []string) *prometheus.Desc { desc := record.Descriptor() return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labelKeys, nil) } // mergeLabels merges the export.Record's labels and resources into a // single set, giving precedence to the record's labels in case of // duplicate keys. This outputs one or both of the keys and the // values as a slice, and either argument may be nil to avoid // allocating an unnecessary slice. func mergeLabels(record export.Record, res *resource.Resource, keys, values *[]string) { if keys != nil { *keys = make([]string, 0, record.Labels().Len()+res.Len()) } if values != nil { *values = make([]string, 0, record.Labels().Len()+res.Len()) } // Duplicate keys are resolved by taking the record label value over // the resource value. mi := attribute.NewMergeIterator(record.Labels(), res.Set()) for mi.Next() { label := mi.Label() if keys != nil { *keys = append(*keys, sanitize(string(label.Key))) } if values != nil { *values = append(*values, label.Value.Emit()) } } } golang-opentelemetry-otel-1.1.0/exporters/prometheus/prometheus_test.go000066400000000000000000000130431414226744000266270ustar00rootroot00000000000000// 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 prometheus_test import ( "context" "fmt" "net/http/httptest" "sort" "strings" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" "go.opentelemetry.io/otel/sdk/resource" ) type expectedMetric struct { kind string name string help string values []string } func (e *expectedMetric) lines() []string { ret := []string{ fmt.Sprintf("# HELP %s %s", e.name, e.help), fmt.Sprintf("# TYPE %s %s", e.name, e.kind), } ret = append(ret, e.values...) return ret } func expectCounterWithHelp(name, help, value string) expectedMetric { return expectedMetric{ kind: "counter", name: name, help: help, values: []string{value}, } } func expectCounter(name, value string) expectedMetric { return expectCounterWithHelp(name, "", value) } func expectGauge(name, value string) expectedMetric { return expectedMetric{ kind: "gauge", name: name, values: []string{value}, } } func expectHistogram(name string, values ...string) expectedMetric { return expectedMetric{ kind: "histogram", name: name, values: values, } } func newPipeline(config prometheus.Config, options ...controller.Option) (*prometheus.Exporter, error) { c := controller.New( processor.NewFactory( selector.NewWithHistogramDistribution( histogram.WithExplicitBoundaries(config.DefaultHistogramBoundaries), ), aggregation.CumulativeTemporalitySelector(), processor.WithMemory(true), ), options..., ) return prometheus.New(config, c) } func TestPrometheusExporter(t *testing.T) { exporter, err := newPipeline( prometheus.Config{ DefaultHistogramBoundaries: []float64{-0.5, 1}, }, controller.WithCollectPeriod(0), controller.WithResource(resource.NewSchemaless(attribute.String("R", "V"))), ) require.NoError(t, err) meter := exporter.MeterProvider().Meter("test") upDownCounter := metric.Must(meter).NewFloat64UpDownCounter("updowncounter") counter := metric.Must(meter).NewFloat64Counter("counter") histogram := metric.Must(meter).NewFloat64Histogram("histogram") labels := []attribute.KeyValue{ attribute.Key("A").String("B"), attribute.Key("C").String("D"), } ctx := context.Background() var expected []expectedMetric counter.Add(ctx, 10, labels...) counter.Add(ctx, 5.3, labels...) expected = append(expected, expectCounter("counter", `counter{A="B",C="D",R="V"} 15.3`)) _ = metric.Must(meter).NewInt64GaugeObserver("intobserver", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(1, labels...) }) expected = append(expected, expectGauge("intobserver", `intobserver{A="B",C="D",R="V"} 1`)) histogram.Record(ctx, -0.6, labels...) histogram.Record(ctx, -0.4, labels...) histogram.Record(ctx, 0.6, labels...) histogram.Record(ctx, 20, labels...) expected = append(expected, expectHistogram("histogram", `histogram_bucket{A="B",C="D",R="V",le="-0.5"} 1`, `histogram_bucket{A="B",C="D",R="V",le="1"} 3`, `histogram_bucket{A="B",C="D",R="V",le="+Inf"} 4`, `histogram_sum{A="B",C="D",R="V"} 19.6`, `histogram_count{A="B",C="D",R="V"} 4`, )) upDownCounter.Add(ctx, 10, labels...) upDownCounter.Add(ctx, -3.2, labels...) expected = append(expected, expectGauge("updowncounter", `updowncounter{A="B",C="D",R="V"} 6.8`)) compareExport(t, exporter, expected) compareExport(t, exporter, expected) } func compareExport(t *testing.T, exporter *prometheus.Exporter, expected []expectedMetric) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/metrics", nil) exporter.ServeHTTP(rec, req) output := rec.Body.String() lines := strings.Split(output, "\n") expectedLines := []string{""} for _, v := range expected { expectedLines = append(expectedLines, v.lines()...) } sort.Strings(lines) sort.Strings(expectedLines) require.Equal(t, expectedLines, lines) } func TestPrometheusStatefulness(t *testing.T) { // Create a meter exporter, err := newPipeline( prometheus.Config{}, controller.WithCollectPeriod(0), controller.WithResource(resource.Empty()), ) require.NoError(t, err) meter := exporter.MeterProvider().Meter("test") ctx := context.Background() counter := metric.Must(meter).NewInt64Counter( "a.counter", metric.WithDescription("Counts things"), ) counter.Add(ctx, 100, attribute.String("key", "value")) compareExport(t, exporter, []expectedMetric{ expectCounterWithHelp("a_counter", "Counts things", `a_counter{key="value"} 100`), }) counter.Add(ctx, 100, attribute.String("key", "value")) compareExport(t, exporter, []expectedMetric{ expectCounterWithHelp("a_counter", "Counts things", `a_counter{key="value"} 200`), }) } golang-opentelemetry-otel-1.1.0/exporters/prometheus/sanitize.go000066400000000000000000000031001414226744000252140ustar00rootroot00000000000000// 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 prometheus // import "go.opentelemetry.io/otel/exporters/prometheus" import ( "strings" "unicode" ) // TODO(paivagustavo): we should provide a more uniform and controlled way of sanitizing. // Letting users define wether we should try or not to sanitize metric names. // This is a copy of sdk/internal/sanitize.go // sanitize returns a string that is truncated to 100 characters if it's too // long, and replaces non-alphanumeric characters to underscores. func sanitize(s string) string { if len(s) == 0 { return s } // TODO(paivagustavo): change this to use a bytes buffer to avoid a large number of string allocations. s = strings.Map(sanitizeRune, s) if unicode.IsDigit(rune(s[0])) { s = "key_" + s } if s[0] == '_' { s = "key" + s } return s } // converts anything that is not a letter or digit to an underscore func sanitizeRune(r rune) rune { if unicode.IsLetter(r) || unicode.IsDigit(r) { return r } // Everything else turns into an underscore return '_' } golang-opentelemetry-otel-1.1.0/exporters/prometheus/sanitize_test.go000066400000000000000000000030051414226744000262570ustar00rootroot00000000000000// 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 prometheus import ( "testing" ) func TestSanitize(t *testing.T) { tests := []struct { name string input string want string }{ { name: "replace character", input: "test/key-1", want: "test_key_1", }, { name: "add prefix if starting with digit", input: "0123456789", want: "key_0123456789", }, { name: "add prefix if starting with _", input: "_0123456789", want: "key_0123456789", }, { name: "starts with _ after sanitization", input: "/0123456789", want: "key_0123456789", }, { name: "valid input", input: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789", want: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got, want := sanitize(tt.input), tt.want; got != want { t.Errorf("sanitize() = %q; want %q", got, want) } }) } } golang-opentelemetry-otel-1.1.0/exporters/stdout/000077500000000000000000000000001414226744000221745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/000077500000000000000000000000001414226744000247225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/config.go000066400000000000000000000053221414226744000265200ustar00rootroot00000000000000// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" import ( "io" "os" "go.opentelemetry.io/otel/attribute" ) var ( defaultWriter = os.Stdout defaultPrettyPrint = false defaultTimestamps = true defaultLabelEncoder = attribute.DefaultEncoder() ) // config contains options for the STDOUT exporter. type config struct { // Writer is the destination. If not set, os.Stdout is used. Writer io.Writer // PrettyPrint will encode the output into readable JSON. Default is // false. PrettyPrint bool // Timestamps specifies if timestamps should be printed. Default is // true. Timestamps bool // LabelEncoder encodes the labels. LabelEncoder attribute.Encoder } // newConfig creates a validated Config configured with options. func newConfig(options ...Option) (config, error) { cfg := config{ Writer: defaultWriter, PrettyPrint: defaultPrettyPrint, Timestamps: defaultTimestamps, LabelEncoder: defaultLabelEncoder, } for _, opt := range options { opt.apply(&cfg) } return cfg, nil } // Option sets the value of an option for a Config. type Option interface { apply(*config) } // WithWriter sets the export stream destination. func WithWriter(w io.Writer) Option { return writerOption{w} } type writerOption struct { W io.Writer } func (o writerOption) apply(cfg *config) { cfg.Writer = o.W } // WithPrettyPrint sets the export stream format to use JSON. func WithPrettyPrint() Option { return prettyPrintOption(true) } type prettyPrintOption bool func (o prettyPrintOption) apply(cfg *config) { cfg.PrettyPrint = bool(o) } // WithoutTimestamps sets the export stream to not include timestamps. func WithoutTimestamps() Option { return timestampsOption(false) } type timestampsOption bool func (o timestampsOption) apply(cfg *config) { cfg.Timestamps = bool(o) } // WithLabelEncoder sets the label encoder used in export. func WithLabelEncoder(enc attribute.Encoder) Option { return labelEncoderOption{enc} } type labelEncoderOption struct { LabelEncoder attribute.Encoder } func (o labelEncoderOption) apply(cfg *config) { cfg.LabelEncoder = o.LabelEncoder } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/doc.go000066400000000000000000000020121414226744000260110ustar00rootroot00000000000000// 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 stdout contains an OpenTelemetry exporter for metric telemetry // to be written to an output destination as JSON. // // This package is currently in a pre-GA phase. Backwards incompatible changes // may be introduced in subsequent minor version releases as we work to track // the evolving OpenTelemetry specification and user feedback. package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/example_test.go000066400000000000000000000052021414226744000277420ustar00rootroot00000000000000// 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 stdoutmetric_test import ( "context" "log" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/global" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) const ( instrumentationName = "github.com/instrumentron" instrumentationVersion = "v0.1.0" ) var ( meter = global.GetMeterProvider().Meter( instrumentationName, metric.WithInstrumentationVersion(instrumentationVersion), ) loopCounter = metric.Must(meter).NewInt64Counter("function.loops") paramValue = metric.Must(meter).NewInt64Histogram("function.param") nameKey = attribute.Key("function.name") ) func add(ctx context.Context, x, y int64) int64 { nameKV := nameKey.String("add") loopCounter.Add(ctx, 1, nameKV) paramValue.Record(ctx, x, nameKV) paramValue.Record(ctx, y, nameKV) return x + y } func multiply(ctx context.Context, x, y int64) int64 { nameKV := nameKey.String("multiply") loopCounter.Add(ctx, 1, nameKV) paramValue.Record(ctx, x, nameKV) paramValue.Record(ctx, y, nameKV) return x * y } func InstallExportPipeline(ctx context.Context) func() { exporter, err := stdoutmetric.New(stdoutmetric.WithPrettyPrint()) if err != nil { log.Fatalf("creating stdoutmetric exporter: %v", err) } pusher := controller.New( processor.NewFactory( simple.NewWithInexpensiveDistribution(), exporter, ), controller.WithExporter(exporter), ) if err = pusher.Start(ctx); err != nil { log.Fatalf("starting push controller: %v", err) } global.SetMeterProvider(pusher) return func() { if err := pusher.Stop(ctx); err != nil { log.Fatalf("stopping push controller: %v", err) } } } func Example() { ctx := context.Background() // Registers a meter Provider globally. cleanup := InstallExportPipeline(ctx) defer cleanup() log.Println("the answer is", add(ctx, multiply(ctx, multiply(ctx, 2, 2), 10), 2)) } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/exporter.go000066400000000000000000000020571414226744000271250ustar00rootroot00000000000000// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" import ( "go.opentelemetry.io/otel/sdk/export/metric" ) type Exporter struct { metricExporter } var ( _ metric.Exporter = &Exporter{} ) // New creates an Exporter with the passed options. func New(options ...Option) (*Exporter, error) { cfg, err := newConfig(options...) if err != nil { return nil, err } return &Exporter{ metricExporter: metricExporter{cfg}, }, nil } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/go.mod000066400000000000000000000055101414226744000260310ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/stdout/stdoutmetric go 1.15 replace ( go.opentelemetry.io/otel => ../../.. go.opentelemetry.io/otel/sdk => ../../../sdk ) require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 go.opentelemetry.io/otel/sdk/metric v0.24.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../prometheus replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ./ replace go.opentelemetry.io/otel/exporters/jaeger => ../../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/go.sum000066400000000000000000000033741414226744000260640ustar00rootroot00000000000000github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/metric.go000066400000000000000000000112161414226744000265350ustar00rootroot00000000000000// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" import ( "context" "encoding/json" "fmt" "strings" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/sdkapi" exportmetric "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) type metricExporter struct { config config } var _ exportmetric.Exporter = &metricExporter{} type line struct { Name string `json:"Name"` Min interface{} `json:"Min,omitempty"` Max interface{} `json:"Max,omitempty"` Sum interface{} `json:"Sum,omitempty"` Count interface{} `json:"Count,omitempty"` LastValue interface{} `json:"Last,omitempty"` // Note: this is a pointer because omitempty doesn't work when time.IsZero() Timestamp *time.Time `json:"Timestamp,omitempty"` } func (e *metricExporter) TemporalityFor(desc *sdkapi.Descriptor, kind aggregation.Kind) aggregation.Temporality { return aggregation.StatelessTemporalitySelector().TemporalityFor(desc, kind) } func (e *metricExporter) Export(_ context.Context, res *resource.Resource, reader exportmetric.InstrumentationLibraryReader) error { var aggError error var batch []line aggError = reader.ForEach(func(lib instrumentation.Library, mr exportmetric.Reader) error { var instLabels []attribute.KeyValue if name := lib.Name; name != "" { instLabels = append(instLabels, attribute.String("instrumentation.name", name)) if version := lib.Version; version != "" { instLabels = append(instLabels, attribute.String("instrumentation.version", version)) } if schema := lib.SchemaURL; schema != "" { instLabels = append(instLabels, attribute.String("instrumentation.schema_url", schema)) } } instSet := attribute.NewSet(instLabels...) encodedInstLabels := instSet.Encoded(e.config.LabelEncoder) return mr.ForEach(e, func(record exportmetric.Record) error { desc := record.Descriptor() agg := record.Aggregation() kind := desc.NumberKind() encodedResource := res.Encoded(e.config.LabelEncoder) var expose line if sum, ok := agg.(aggregation.Sum); ok { value, err := sum.Sum() if err != nil { return err } expose.Sum = value.AsInterface(kind) } if mmsc, ok := agg.(aggregation.MinMaxSumCount); ok { count, err := mmsc.Count() if err != nil { return err } expose.Count = count max, err := mmsc.Max() if err != nil { return err } expose.Max = max.AsInterface(kind) min, err := mmsc.Min() if err != nil { return err } expose.Min = min.AsInterface(kind) } else if lv, ok := agg.(aggregation.LastValue); ok { value, timestamp, err := lv.LastValue() if err != nil { return err } expose.LastValue = value.AsInterface(kind) if e.config.Timestamps { expose.Timestamp = ×tamp } } var encodedLabels string iter := record.Labels().Iter() if iter.Len() > 0 { encodedLabels = record.Labels().Encoded(e.config.LabelEncoder) } var sb strings.Builder sb.WriteString(desc.Name()) if len(encodedLabels) > 0 || len(encodedResource) > 0 || len(encodedInstLabels) > 0 { sb.WriteRune('{') sb.WriteString(encodedResource) if len(encodedInstLabels) > 0 && len(encodedResource) > 0 { sb.WriteRune(',') } sb.WriteString(encodedInstLabels) if len(encodedLabels) > 0 && (len(encodedInstLabels) > 0 || len(encodedResource) > 0) { sb.WriteRune(',') } sb.WriteString(encodedLabels) sb.WriteRune('}') } expose.Name = sb.String() batch = append(batch, expose) return nil }) }) if len(batch) == 0 { return aggError } data, err := e.marshal(batch) if err != nil { return err } fmt.Fprintln(e.config.Writer, string(data)) return aggError } // marshal v with appropriate indentation. func (e *metricExporter) marshal(v interface{}) ([]byte, error) { if e.config.PrettyPrint { return json.MarshalIndent(v, "", "\t") } return json.Marshal(v) } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdoutmetric/metric_test.go000066400000000000000000000176251414226744000276060ustar00rootroot00000000000000// 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 stdoutmetric_test import ( "bytes" "context" "encoding/json" "fmt" "strings" "testing" "time" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) type testFixture struct { t *testing.T ctx context.Context cont *controller.Controller meter metric.Meter exporter *stdoutmetric.Exporter output *bytes.Buffer } var testResource = resource.NewSchemaless(attribute.String("R", "V")) func newFixture(t *testing.T, opts ...stdoutmetric.Option) testFixture { return newFixtureWithResource(t, testResource, opts...) } func newFixtureWithResource(t *testing.T, res *resource.Resource, opts ...stdoutmetric.Option) testFixture { buf := &bytes.Buffer{} opts = append(opts, stdoutmetric.WithWriter(buf)) opts = append(opts, stdoutmetric.WithoutTimestamps()) exp, err := stdoutmetric.New(opts...) if err != nil { t.Fatal("Error building fixture: ", err) } aggSel := processortest.AggregatorSelector() proc := processor.NewFactory(aggSel, aggregation.StatelessTemporalitySelector()) cont := controller.New(proc, controller.WithExporter(exp), controller.WithResource(res), ) ctx := context.Background() require.NoError(t, cont.Start(ctx)) meter := cont.Meter("test") return testFixture{ t: t, ctx: ctx, exporter: exp, cont: cont, meter: meter, output: buf, } } func (fix testFixture) Output() string { return strings.TrimSpace(fix.output.String()) } func TestStdoutTimestamp(t *testing.T) { var buf bytes.Buffer aggSel := processortest.AggregatorSelector() proc := processor.NewFactory(aggSel, aggregation.CumulativeTemporalitySelector()) exporter, err := stdoutmetric.New( stdoutmetric.WithWriter(&buf), ) if err != nil { t.Fatal("Invalid config: ", err) } cont := controller.New(proc, controller.WithExporter(exporter), controller.WithResource(testResource), ) ctx := context.Background() require.NoError(t, cont.Start(ctx)) meter := cont.Meter("test") counter := metric.Must(meter).NewInt64Counter("name.lastvalue") before := time.Now() // Ensure the timestamp is after before. time.Sleep(time.Nanosecond) counter.Add(ctx, 1) require.NoError(t, cont.Stop(ctx)) // Ensure the timestamp is before after. time.Sleep(time.Nanosecond) after := time.Now() var printed []interface{} if err := json.Unmarshal(buf.Bytes(), &printed); err != nil { t.Fatal("JSON parse error: ", err) } require.Len(t, printed, 1) lastValue, ok := printed[0].(map[string]interface{}) require.True(t, ok, "last value format") require.Contains(t, lastValue, "Timestamp") lastValueTS := lastValue["Timestamp"].(string) lastValueTimestamp, err := time.Parse(time.RFC3339Nano, lastValueTS) if err != nil { t.Fatal("JSON parse error: ", lastValueTS, ": ", err) } assert.True(t, lastValueTimestamp.After(before)) assert.True(t, lastValueTimestamp.Before(after)) } func TestStdoutCounterFormat(t *testing.T) { fix := newFixture(t) counter := metric.Must(fix.meter).NewInt64Counter("name.sum") counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D")) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, `[{"Name":"name.sum{R=V,instrumentation.name=test,A=B,C=D}","Sum":123}]`, fix.Output()) } func TestStdoutLastValueFormat(t *testing.T) { fix := newFixture(t) counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D")) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, `[{"Name":"name.lastvalue{R=V,instrumentation.name=test,A=B,C=D}","Last":123.456}]`, fix.Output()) } func TestStdoutMinMaxSumCount(t *testing.T) { fix := newFixture(t) counter := metric.Must(fix.meter).NewFloat64Counter("name.minmaxsumcount") counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D")) counter.Add(fix.ctx, 876.543, attribute.String("A", "B"), attribute.String("C", "D")) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, `[{"Name":"name.minmaxsumcount{R=V,instrumentation.name=test,A=B,C=D}","Min":123.456,"Max":876.543,"Sum":999.999,"Count":2}]`, fix.Output()) } func TestStdoutHistogramFormat(t *testing.T) { fix := newFixture(t, stdoutmetric.WithPrettyPrint()) inst := metric.Must(fix.meter).NewFloat64Histogram("name.histogram") for i := 0; i < 1000; i++ { inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D")) } require.NoError(t, fix.cont.Stop(fix.ctx)) // TODO: Stdout does not export `Count` for histogram, nor the buckets. require.Equal(t, `[ { "Name": "name.histogram{R=V,instrumentation.name=test,A=B,C=D}", "Sum": 500000 } ]`, fix.Output()) } func TestStdoutNoData(t *testing.T) { runTwoAggs := func(aggName string) { t.Run(aggName, func(t *testing.T) { t.Parallel() fix := newFixture(t) _ = metric.Must(fix.meter).NewFloat64Counter(fmt.Sprint("name.", aggName)) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, "", fix.Output()) }) } runTwoAggs("lastvalue") runTwoAggs("minmaxsumcount") } func TestStdoutResource(t *testing.T) { type testCase struct { name string expect string res *resource.Resource attrs []attribute.KeyValue } newCase := func(name, expect string, res *resource.Resource, attrs ...attribute.KeyValue) testCase { return testCase{ name: name, expect: expect, res: res, attrs: attrs, } } testCases := []testCase{ newCase("resource and attribute", "R1=V1,R2=V2,instrumentation.name=test,A=B,C=D", resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), attribute.String("A", "B"), attribute.String("C", "D")), newCase("only resource", "R1=V1,R2=V2,instrumentation.name=test", resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), ), newCase("empty resource", "instrumentation.name=test,A=B,C=D", resource.Empty(), attribute.String("A", "B"), attribute.String("C", "D"), ), newCase("default resource", fmt.Sprint(resource.Default().Encoded(attribute.DefaultEncoder()), ",instrumentation.name=test,A=B,C=D"), resource.Default(), attribute.String("A", "B"), attribute.String("C", "D"), ), // We explicitly do not de-duplicate between resources // and metric labels in this exporter. newCase("resource deduplication", "R1=V1,R2=V2,instrumentation.name=test,R1=V3,R2=V4", resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), attribute.String("R1", "V3"), attribute.String("R2", "V4")), } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() fix := newFixtureWithResource(t, tc.res) counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") counter.Add(ctx, 123.456, tc.attrs...) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, `[{"Name":"name.lastvalue{`+tc.expect+`}","Last":123.456}]`, fix.Output()) }) } } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/000077500000000000000000000000001414226744000245355ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/config.go000066400000000000000000000043271414226744000263370ustar00rootroot00000000000000// 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 stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" import ( "io" "os" ) var ( defaultWriter = os.Stdout defaultPrettyPrint = false defaultTimestamps = true ) // config contains options for the STDOUT exporter. type config struct { // Writer is the destination. If not set, os.Stdout is used. Writer io.Writer // PrettyPrint will encode the output into readable JSON. Default is // false. PrettyPrint bool // Timestamps specifies if timestamps should be printed. Default is // true. Timestamps bool } // newConfig creates a validated Config configured with options. func newConfig(options ...Option) (config, error) { cfg := config{ Writer: defaultWriter, PrettyPrint: defaultPrettyPrint, Timestamps: defaultTimestamps, } for _, opt := range options { opt.apply(&cfg) } return cfg, nil } // Option sets the value of an option for a Config. type Option interface { apply(*config) } // WithWriter sets the export stream destination. func WithWriter(w io.Writer) Option { return writerOption{w} } type writerOption struct { W io.Writer } func (o writerOption) apply(cfg *config) { cfg.Writer = o.W } // WithPrettyPrint sets the export stream format to use JSON. func WithPrettyPrint() Option { return prettyPrintOption(true) } type prettyPrintOption bool func (o prettyPrintOption) apply(cfg *config) { cfg.PrettyPrint = bool(o) } // WithoutTimestamps sets the export stream to not include timestamps. func WithoutTimestamps() Option { return timestampsOption(false) } type timestampsOption bool func (o timestampsOption) apply(cfg *config) { cfg.Timestamps = bool(o) } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/doc.go000066400000000000000000000014521414226744000256330ustar00rootroot00000000000000// 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 stdout contains an OpenTelemetry exporter for tracing // telemetry to be written to an output destination as JSON. package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/example_test.go000066400000000000000000000045561414226744000275700ustar00rootroot00000000000000// 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 stdouttrace_test import ( "context" "log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) const ( instrumentationName = "github.com/instrumentron" instrumentationVersion = "v0.1.0" ) var ( tracer = otel.GetTracerProvider().Tracer( instrumentationName, trace.WithInstrumentationVersion(instrumentationVersion), trace.WithSchemaURL(semconv.SchemaURL), ) ) func add(ctx context.Context, x, y int64) int64 { var span trace.Span _, span = tracer.Start(ctx, "Addition") defer span.End() return x + y } func multiply(ctx context.Context, x, y int64) int64 { var span trace.Span _, span = tracer.Start(ctx, "Multiplication") defer span.End() return x * y } func Resource() *resource.Resource { return resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("stdout-example"), semconv.ServiceVersionKey.String("0.0.1"), ) } func InstallExportPipeline(ctx context.Context) func() { exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil { log.Fatalf("creating stdout exporter: %v", err) } tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(Resource()), ) otel.SetTracerProvider(tracerProvider) return func() { if err := tracerProvider.Shutdown(ctx); err != nil { log.Fatalf("stopping tracer provider: %v", err) } } } func Example() { ctx := context.Background() // Registers a tracer Provider globally. cleanup := InstallExportPipeline(ctx) defer cleanup() log.Println("the answer is", add(ctx, multiply(ctx, multiply(ctx, 2, 2), 10), 2)) } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/go.mod000066400000000000000000000053451414226744000256520ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/stdout/stdouttrace go 1.15 replace ( go.opentelemetry.io/otel => ../../.. go.opentelemetry.io/otel/sdk => ../../../sdk ) require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../prometheus replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ./ replace go.opentelemetry.io/otel/exporters/jaeger => ../../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdoutmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/go.sum000066400000000000000000000031151414226744000256700ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/trace.go000066400000000000000000000046161414226744000261710ustar00rootroot00000000000000// 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 stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" import ( "context" "encoding/json" "sync" "time" "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" ) var zeroTime time.Time var _ trace.SpanExporter = &Exporter{} // New creates an Exporter with the passed options. func New(options ...Option) (*Exporter, error) { cfg, err := newConfig(options...) if err != nil { return nil, err } enc := json.NewEncoder(cfg.Writer) if cfg.PrettyPrint { enc.SetIndent("", "\t") } return &Exporter{ encoder: enc, timestamps: cfg.Timestamps, }, nil } // Exporter is an implementation of trace.SpanSyncer that writes spans to stdout. type Exporter struct { encoder *json.Encoder encoderMu sync.Mutex timestamps bool stoppedMu sync.RWMutex stopped bool } // ExportSpans writes spans in json format to stdout. func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { e.stoppedMu.RLock() stopped := e.stopped e.stoppedMu.RUnlock() if stopped { return nil } if len(spans) == 0 { return nil } stubs := tracetest.SpanStubsFromReadOnlySpans(spans) e.encoderMu.Lock() defer e.encoderMu.Unlock() for i := range stubs { stub := &stubs[i] // Remove timestamps if !e.timestamps { stub.StartTime = zeroTime stub.EndTime = zeroTime for j := range stub.Events { ev := &stub.Events[j] ev.Time = zeroTime } } // Encode span stubs, one by one if err := e.encoder.Encode(stub); err != nil { return err } } return nil } // Shutdown is called to stop the exporter, it preforms no action. func (e *Exporter) Shutdown(ctx context.Context) error { e.stoppedMu.Lock() e.stopped = true e.stoppedMu.Unlock() select { case <-ctx.Done(): return ctx.Err() default: } return nil } golang-opentelemetry-otel-1.1.0/exporters/stdout/stdouttrace/trace_test.go000066400000000000000000000127051414226744000272260ustar00rootroot00000000000000// 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 stdouttrace_test import ( "bytes" "context" "encoding/json" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) func TestExporter_ExportSpan(t *testing.T) { // setup test span now := time.Now() traceID, _ := trace.TraceIDFromHex("0102030405060708090a0b0c0d0e0f10") spanID, _ := trace.SpanIDFromHex("0102030405060708") traceState, _ := trace.ParseTraceState("key=val") keyValue := "value" doubleValue := 123.456 resource := resource.NewSchemaless(attribute.String("rk1", "rv11")) ss := tracetest.SpanStub{ SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceState: traceState, }), Name: "/foo", StartTime: now, EndTime: now, Attributes: []attribute.KeyValue{ attribute.String("key", keyValue), attribute.Float64("double", doubleValue), }, Events: []tracesdk.Event{ {Name: "foo", Attributes: []attribute.KeyValue{attribute.String("key", keyValue)}, Time: now}, {Name: "bar", Attributes: []attribute.KeyValue{attribute.Float64("double", doubleValue)}, Time: now}, }, SpanKind: trace.SpanKindInternal, Status: tracesdk.Status{ Code: codes.Error, Description: "interesting", }, Resource: resource, } tests := []struct { opts []stdouttrace.Option expectNow time.Time }{ { opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint()}, expectNow: now, }, { opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint(), stdouttrace.WithoutTimestamps()}, // expectNow is an empty time.Time }, } ctx := context.Background() for _, tt := range tests { // write to buffer for testing var b bytes.Buffer ex, err := stdouttrace.New(append(tt.opts, stdouttrace.WithWriter(&b))...) require.Nil(t, err) err = ex.ExportSpans(ctx, tracetest.SpanStubs{ss, ss}.Snapshots()) require.Nil(t, err) got := b.String() wantone := expectedJSON(tt.expectNow) assert.Equal(t, wantone+wantone, got) } } func expectedJSON(now time.Time) string { serializedNow, _ := json.Marshal(now) return `{ "Name": "/foo", "SpanContext": { "TraceID": "0102030405060708090a0b0c0d0e0f10", "SpanID": "0102030405060708", "TraceFlags": "00", "TraceState": "key=val", "Remote": false }, "Parent": { "TraceID": "00000000000000000000000000000000", "SpanID": "0000000000000000", "TraceFlags": "00", "TraceState": "", "Remote": false }, "SpanKind": 1, "StartTime": ` + string(serializedNow) + `, "EndTime": ` + string(serializedNow) + `, "Attributes": [ { "Key": "key", "Value": { "Type": "STRING", "Value": "value" } }, { "Key": "double", "Value": { "Type": "FLOAT64", "Value": 123.456 } } ], "Events": [ { "Name": "foo", "Attributes": [ { "Key": "key", "Value": { "Type": "STRING", "Value": "value" } } ], "DroppedAttributeCount": 0, "Time": ` + string(serializedNow) + ` }, { "Name": "bar", "Attributes": [ { "Key": "double", "Value": { "Type": "FLOAT64", "Value": 123.456 } } ], "DroppedAttributeCount": 0, "Time": ` + string(serializedNow) + ` } ], "Links": null, "Status": { "Code": "Error", "Description": "interesting" }, "DroppedAttributes": 0, "DroppedEvents": 0, "DroppedLinks": 0, "ChildSpanCount": 0, "Resource": [ { "Key": "rk1", "Value": { "Type": "STRING", "Value": "rv11" } } ], "InstrumentationLibrary": { "Name": "", "Version": "", "SchemaURL": "" } } ` } func TestExporterShutdownHonorsTimeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e, err := stdouttrace.New() if err != nil { t.Fatalf("failed to create exporter: %v", err) } innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond) defer innerCancel() <-innerCtx.Done() err = e.Shutdown(innerCtx) assert.ErrorIs(t, err, context.DeadlineExceeded) } func TestExporterShutdownHonorsCancel(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() e, err := stdouttrace.New() if err != nil { t.Fatalf("failed to create exporter: %v", err) } innerCtx, innerCancel := context.WithCancel(ctx) innerCancel() err = e.Shutdown(innerCtx) assert.ErrorIs(t, err, context.Canceled) } func TestExporterShutdownNoError(t *testing.T) { e, err := stdouttrace.New() if err != nil { t.Fatalf("failed to create exporter: %v", err) } if err := e.Shutdown(context.Background()); err != nil { t.Errorf("shutdown errored: expected nil, got %v", err) } } golang-opentelemetry-otel-1.1.0/exporters/zipkin/000077500000000000000000000000001414226744000221565ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/exporters/zipkin/doc.go000066400000000000000000000013441414226744000232540ustar00rootroot00000000000000// 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 zipkin contains an OpenTelemetry tracing exporter for Zipkin. package zipkin // import "go.opentelemetry.io/otel/exporters/zipkin" golang-opentelemetry-otel-1.1.0/exporters/zipkin/go.mod000066400000000000000000000054351414226744000232730ustar00rootroot00000000000000module go.opentelemetry.io/otel/exporters/zipkin go 1.15 require ( github.com/google/go-cmp v0.5.6 github.com/openzipkin/zipkin-go v0.2.5 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../prometheus replace go.opentelemetry.io/otel/exporters/otlp => ../otlp replace go.opentelemetry.io/otel/exporters/jaeger => ../jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ./ replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/exporters/zipkin/go.sum000066400000000000000000000252761414226744000233250ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= golang-opentelemetry-otel-1.1.0/exporters/zipkin/model.go000066400000000000000000000201401414226744000236020ustar00rootroot00000000000000// 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 zipkin // import "go.opentelemetry.io/otel/exporters/zipkin" import ( "encoding/binary" "encoding/json" "fmt" "net" "strconv" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" zkmodel "github.com/openzipkin/zipkin-go/model" sdktrace "go.opentelemetry.io/otel/sdk/trace" tracesdk "go.opentelemetry.io/otel/sdk/trace" ) const ( keyInstrumentationLibraryName = "otel.library.name" keyInstrumentationLibraryVersion = "otel.library.version" keyPeerHostname attribute.Key = "peer.hostname" keyPeerAddress attribute.Key = "peer.address" ) var defaultServiceName string func init() { // fetch service.name from default resource for backup defaultResource := resource.Default() if value, exists := defaultResource.Set().Value(semconv.ServiceNameKey); exists { defaultServiceName = value.AsString() } } // SpanModels converts OpenTelemetry spans into Zipkin model spans. // This is used for exporting to Zipkin compatible tracing services. func SpanModels(batch []tracesdk.ReadOnlySpan) []zkmodel.SpanModel { models := make([]zkmodel.SpanModel, 0, len(batch)) for _, data := range batch { models = append(models, toZipkinSpanModel(data)) } return models } func getServiceName(attrs []attribute.KeyValue) string { for _, kv := range attrs { if kv.Key == semconv.ServiceNameKey { return kv.Value.AsString() } } return defaultServiceName } func toZipkinSpanModel(data tracesdk.ReadOnlySpan) zkmodel.SpanModel { return zkmodel.SpanModel{ SpanContext: toZipkinSpanContext(data), Name: data.Name(), Kind: toZipkinKind(data.SpanKind()), Timestamp: data.StartTime(), Duration: data.EndTime().Sub(data.StartTime()), Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: getServiceName(data.Resource().Attributes()), }, RemoteEndpoint: toZipkinRemoteEndpoint(data), Annotations: toZipkinAnnotations(data.Events()), Tags: toZipkinTags(data), } } func toZipkinSpanContext(data tracesdk.ReadOnlySpan) zkmodel.SpanContext { return zkmodel.SpanContext{ TraceID: toZipkinTraceID(data.SpanContext().TraceID()), ID: toZipkinID(data.SpanContext().SpanID()), ParentID: toZipkinParentID(data.Parent().SpanID()), Debug: false, Sampled: nil, Err: nil, } } func toZipkinTraceID(traceID trace.TraceID) zkmodel.TraceID { return zkmodel.TraceID{ High: binary.BigEndian.Uint64(traceID[:8]), Low: binary.BigEndian.Uint64(traceID[8:]), } } func toZipkinID(spanID trace.SpanID) zkmodel.ID { return zkmodel.ID(binary.BigEndian.Uint64(spanID[:])) } func toZipkinParentID(spanID trace.SpanID) *zkmodel.ID { if spanID.IsValid() { id := toZipkinID(spanID) return &id } return nil } func toZipkinKind(kind trace.SpanKind) zkmodel.Kind { switch kind { case trace.SpanKindUnspecified: return zkmodel.Undetermined case trace.SpanKindInternal: // The spec says we should set the kind to nil, but // the model does not allow that. return zkmodel.Undetermined case trace.SpanKindServer: return zkmodel.Server case trace.SpanKindClient: return zkmodel.Client case trace.SpanKindProducer: return zkmodel.Producer case trace.SpanKindConsumer: return zkmodel.Consumer } return zkmodel.Undetermined } func toZipkinAnnotations(events []tracesdk.Event) []zkmodel.Annotation { if len(events) == 0 { return nil } annotations := make([]zkmodel.Annotation, 0, len(events)) for _, event := range events { value := event.Name if len(event.Attributes) > 0 { jsonString := attributesToJSONMapString(event.Attributes) if jsonString != "" { value = fmt.Sprintf("%s: %s", event.Name, jsonString) } } annotations = append(annotations, zkmodel.Annotation{ Timestamp: event.Time, Value: value, }) } return annotations } func attributesToJSONMapString(attributes []attribute.KeyValue) string { m := make(map[string]interface{}, len(attributes)) for _, attribute := range attributes { m[(string)(attribute.Key)] = attribute.Value.AsInterface() } // if an error happens, the result will be an empty string jsonBytes, _ := json.Marshal(m) return (string)(jsonBytes) } // extraZipkinTags are those that may be added to every outgoing span var extraZipkinTags = []string{ "otel.status_code", keyInstrumentationLibraryName, keyInstrumentationLibraryVersion, } func toZipkinTags(data tracesdk.ReadOnlySpan) map[string]string { attr := data.Attributes() m := make(map[string]string, len(attr)+len(extraZipkinTags)) for _, kv := range attr { switch kv.Value.Type() { // For slice attributes, serialize as JSON list string. case attribute.BOOLSLICE: json, _ := json.Marshal(kv.Value.AsBoolSlice()) m[(string)(kv.Key)] = (string)(json) case attribute.INT64SLICE: json, _ := json.Marshal(kv.Value.AsInt64Slice()) m[(string)(kv.Key)] = (string)(json) case attribute.FLOAT64SLICE: json, _ := json.Marshal(kv.Value.AsFloat64Slice()) m[(string)(kv.Key)] = (string)(json) case attribute.STRINGSLICE: json, _ := json.Marshal(kv.Value.AsStringSlice()) m[(string)(kv.Key)] = (string)(json) default: m[(string)(kv.Key)] = kv.Value.Emit() } } if data.Status().Code != codes.Unset { m["otel.status_code"] = data.Status().Code.String() } if data.Status().Code == codes.Error { m["error"] = data.Status().Description } else { delete(m, "error") } if il := data.InstrumentationLibrary(); il.Name != "" { m[keyInstrumentationLibraryName] = il.Name if il.Version != "" { m[keyInstrumentationLibraryVersion] = il.Version } } if len(m) == 0 { return nil } return m } // Rank determines selection order for remote endpoint. See the specification // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/trace/sdk_exporters/zipkin.md#otlp---zipkin var remoteEndpointKeyRank = map[attribute.Key]int{ semconv.PeerServiceKey: 0, semconv.NetPeerNameKey: 1, semconv.NetPeerIPKey: 2, keyPeerHostname: 3, keyPeerAddress: 4, semconv.HTTPHostKey: 5, semconv.DBNameKey: 6, } func toZipkinRemoteEndpoint(data sdktrace.ReadOnlySpan) *zkmodel.Endpoint { // Should be set only for client or producer kind if sk := data.SpanKind(); sk != trace.SpanKindClient && sk != trace.SpanKindProducer { return nil } attr := data.Attributes() var endpointAttr attribute.KeyValue for _, kv := range attr { rank, ok := remoteEndpointKeyRank[kv.Key] if !ok { continue } currentKeyRank, ok := remoteEndpointKeyRank[endpointAttr.Key] if ok && rank < currentKeyRank { endpointAttr = kv } else if !ok { endpointAttr = kv } } if endpointAttr.Key == "" { return nil } if endpointAttr.Key != semconv.NetPeerIPKey && endpointAttr.Value.Type() == attribute.STRING { return &zkmodel.Endpoint{ ServiceName: endpointAttr.Value.AsString(), } } return remoteEndpointPeerIPWithPort(endpointAttr.Value.AsString(), attr) } // Handles `net.peer.ip` remote endpoint separately (should include `net.peer.ip` // as well, if available). func remoteEndpointPeerIPWithPort(peerIP string, attrs []attribute.KeyValue) *zkmodel.Endpoint { ip := net.ParseIP(peerIP) if ip == nil { return nil } endpoint := &zkmodel.Endpoint{} // Determine if IPv4 or IPv6 if ip.To4() != nil { endpoint.IPv4 = ip } else { endpoint.IPv6 = ip } for _, kv := range attrs { if kv.Key == semconv.NetPeerPortKey { port, _ := strconv.ParseUint(kv.Value.Emit(), 10, 16) endpoint.Port = uint16(port) return endpoint } } return endpoint } golang-opentelemetry-otel-1.1.0/exporters/zipkin/model_test.go000066400000000000000000000705701414226744000246550ustar00rootroot00000000000000// 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 zipkin import ( "fmt" "net" "strconv" "testing" "time" "github.com/google/go-cmp/cmp" zkmodel "github.com/openzipkin/zipkin-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) func TestModelConversion(t *testing.T) { resource := resource.NewSchemaless( semconv.ServiceNameKey.String("model-test"), ) inputBatch := tracetest.SpanStubs{ // typical span data { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindServer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), attribute.IntSlice("attr3", []int{0, 1, 2}), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data with no parent (same as typical, but has // invalid parent) { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), SpanKind: trace.SpanKindServer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data of unspecified kind { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindUnspecified, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data of internal kind { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindInternal, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data of client kind { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindClient, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), attribute.String("peer.hostname", "test-peer-hostname"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int64("net.peer.port", 9876), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data of producer kind { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindProducer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data of consumer kind { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindConsumer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data with no events { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindServer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.Int64("attr1", 42), attribute.String("attr2", "bar"), }, Events: nil, Status: tracesdk.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // span data with an "error" attribute set to "false" { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, }), SpanKind: trace.SpanKindServer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: []attribute.KeyValue{ attribute.String("error", "false"), }, Events: []tracesdk.Event{ { Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Name: "ev1", Attributes: []attribute.KeyValue{ attribute.Int64("eventattr1", 123), }, }, { Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Name: "ev2", Attributes: nil, }, }, Resource: resource, }, }.Snapshots() expectedOutputBatch := []zkmodel.SpanModel{ // model for typical span data { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "attr3": "[0,1,2]", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data with no parent { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: nil, Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data of unspecified kind { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data of internal kind { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data of client kind { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "CLIENT", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: &zkmodel.Endpoint{ IPv4: net.ParseIP("1.2.3.4"), Port: 9876, }, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "net.peer.ip": "1.2.3.4", "net.peer.port": "9876", "peer.hostname": "test-peer-hostname", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data of producer kind { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "PRODUCER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data of consumer kind { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "CONSUMER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data with no events { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: nil, Tags: map[string]string{ "attr1": "42", "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", }, }, // model for span data with an "error" attribute set to "false" { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "model-test", }, RemoteEndpoint: nil, Annotations: []zkmodel.Annotation{ { Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), Value: `ev1: {"eventattr1":123}`, }, { Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Value: "ev2", }, }, Tags: nil, // should be omitted }, } gottenOutputBatch := SpanModels(inputBatch) require.Equal(t, expectedOutputBatch, gottenOutputBatch) } func zkmodelIDPtr(n uint64) *zkmodel.ID { id := zkmodel.ID(n) return &id } func TestTagsTransformation(t *testing.T) { keyValue := "value" doubleValue := 123.456 uintValue := int64(123) statusMessage := "this is a problem" instrLibName := "instrumentation-library" instrLibVersion := "semver:1.0.0" tests := []struct { name string data tracetest.SpanStub want map[string]string }{ { name: "attributes", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{ attribute.String("key", keyValue), attribute.Float64("double", doubleValue), attribute.Int64("uint", uintValue), attribute.Bool("ok", true), }, }, want: map[string]string{ "double": fmt.Sprint(doubleValue), "key": keyValue, "ok": "true", "uint": strconv.FormatInt(uintValue, 10), }, }, { name: "no attributes", data: tracetest.SpanStub{}, want: nil, }, { name: "omit-noerror", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{ attribute.Bool("error", false), }, }, want: nil, }, { name: "statusCode", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{ attribute.String("key", keyValue), attribute.Bool("error", true), }, Status: tracesdk.Status{ Code: codes.Error, Description: statusMessage, }, }, want: map[string]string{ "error": statusMessage, "key": keyValue, "otel.status_code": codes.Error.String(), }, }, { name: "instrLib-empty", data: tracetest.SpanStub{ InstrumentationLibrary: instrumentation.Library{}, }, want: nil, }, { name: "instrLib-noversion", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{}, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, }, }, want: map[string]string{ "otel.library.name": instrLibName, }, }, { name: "instrLib-with-version", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{}, InstrumentationLibrary: instrumentation.Library{ Name: instrLibName, Version: instrLibVersion, }, }, want: map[string]string{ "otel.library.name": instrLibName, "otel.library.version": instrLibVersion, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := toZipkinTags(tt.data.Snapshot()) if diff := cmp.Diff(got, tt.want); diff != "" { t.Errorf("Diff%v", diff) } }) } } func TestRemoteEndpointTransformation(t *testing.T) { tests := []struct { name string data tracetest.SpanStub want *zkmodel.Endpoint }{ { name: "nil-not-applicable", data: tracetest.SpanStub{ SpanKind: trace.SpanKindClient, Attributes: []attribute.KeyValue{}, }, want: nil, }, { name: "nil-not-found", data: tracetest.SpanStub{ SpanKind: trace.SpanKindConsumer, Attributes: []attribute.KeyValue{ attribute.String("attr", "test"), }, }, want: nil, }, { name: "peer-service-rank", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ semconv.PeerServiceKey.String("peer-service-test"), semconv.NetPeerNameKey.String("peer-name-test"), semconv.HTTPHostKey.String("http-host-test"), }, }, want: &zkmodel.Endpoint{ ServiceName: "peer-service-test", }, }, { name: "http-host-rank", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ semconv.HTTPHostKey.String("http-host-test"), semconv.DBNameKey.String("db-name-test"), }, }, want: &zkmodel.Endpoint{ ServiceName: "http-host-test", }, }, { name: "db-name-rank", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ attribute.String("foo", "bar"), semconv.DBNameKey.String("db-name-test"), }, }, want: &zkmodel.Endpoint{ ServiceName: "db-name-test", }, }, { name: "peer-hostname-rank", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ keyPeerHostname.String("peer-hostname-test"), keyPeerAddress.String("peer-address-test"), semconv.HTTPHostKey.String("http-host-test"), semconv.DBNameKey.String("http-host-test"), }, }, want: &zkmodel.Endpoint{ ServiceName: "peer-hostname-test", }, }, { name: "peer-address-rank", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ keyPeerAddress.String("peer-address-test"), semconv.HTTPHostKey.String("http-host-test"), semconv.DBNameKey.String("http-host-test"), }, }, want: &zkmodel.Endpoint{ ServiceName: "peer-address-test", }, }, { name: "net-peer-invalid-ip", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ semconv.NetPeerIPKey.String("INVALID"), }, }, want: nil, }, { name: "net-peer-ipv6-no-port", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ semconv.NetPeerIPKey.String("0:0:1:5ee:bad:c0de:0:0"), }, }, want: &zkmodel.Endpoint{ IPv6: net.ParseIP("0:0:1:5ee:bad:c0de:0:0"), }, }, { name: "net-peer-ipv4-port", data: tracetest.SpanStub{ SpanKind: trace.SpanKindProducer, Attributes: []attribute.KeyValue{ semconv.NetPeerIPKey.String("1.2.3.4"), semconv.NetPeerPortKey.Int(9876), }, }, want: &zkmodel.Endpoint{ IPv4: net.ParseIP("1.2.3.4"), Port: 9876, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := toZipkinRemoteEndpoint(tt.data.Snapshot()) if diff := cmp.Diff(got, tt.want); diff != "" { t.Errorf("Diff%v", diff) } }) } } func TestServiceName(t *testing.T) { attrs := []attribute.KeyValue{} assert.Equal(t, defaultServiceName, getServiceName(attrs)) attrs = append(attrs, attribute.String("test_key", "test_value")) assert.Equal(t, defaultServiceName, getServiceName(attrs)) attrs = append(attrs, semconv.ServiceNameKey.String("my_service")) assert.Equal(t, "my_service", getServiceName(attrs)) } golang-opentelemetry-otel-1.1.0/exporters/zipkin/zipkin.go000066400000000000000000000106231414226744000240130ustar00rootroot00000000000000// 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 zipkin // import "go.opentelemetry.io/otel/exporters/zipkin" import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "io/ioutil" "log" "net/http" "net/url" "sync" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) // Exporter exports spans to the zipkin collector. type Exporter struct { url string client *http.Client logger *log.Logger config config stoppedMu sync.RWMutex stopped bool } var ( _ sdktrace.SpanExporter = &Exporter{} ) // Options contains configuration for the exporter. type config struct { client *http.Client logger *log.Logger } // Option defines a function that configures the exporter. type Option interface { apply(*config) } type optionFunc func(*config) func (fn optionFunc) apply(cfg *config) { fn(cfg) } // WithLogger configures the exporter to use the passed logger. func WithLogger(logger *log.Logger) Option { return optionFunc(func(cfg *config) { cfg.logger = logger }) } // WithClient configures the exporter to use the passed HTTP client. func WithClient(client *http.Client) Option { return optionFunc(func(cfg *config) { cfg.client = client }) } // New creates a new Zipkin exporter. func New(collectorURL string, opts ...Option) (*Exporter, error) { if collectorURL == "" { return nil, errors.New("collector URL cannot be empty") } u, err := url.Parse(collectorURL) if err != nil { return nil, fmt.Errorf("invalid collector URL %q: %v", collectorURL, err) } if u.Scheme == "" || u.Host == "" { return nil, fmt.Errorf("invalid collector URL %q: no scheme or host", collectorURL) } cfg := config{} for _, opt := range opts { opt.apply(&cfg) } if cfg.client == nil { cfg.client = http.DefaultClient } return &Exporter{ url: collectorURL, client: cfg.client, logger: cfg.logger, config: cfg, }, nil } // ExportSpans exports spans to a Zipkin receiver. func (e *Exporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { e.stoppedMu.RLock() stopped := e.stopped e.stoppedMu.RUnlock() if stopped { e.logf("exporter stopped, not exporting span batch") return nil } if len(spans) == 0 { e.logf("no spans to export") return nil } models := SpanModels(spans) body, err := json.Marshal(models) if err != nil { return e.errf("failed to serialize zipkin models to JSON: %v", err) } e.logf("about to send a POST request to %s with body %s", e.url, body) req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.url, bytes.NewBuffer(body)) if err != nil { return e.errf("failed to create request to %s: %v", e.url, err) } req.Header.Set("Content-Type", "application/json") resp, err := e.client.Do(req) if err != nil { return e.errf("request to %s failed: %v", e.url, err) } defer resp.Body.Close() // Zipkin API returns a 202 on success and the content of the body isn't interesting // but it is still being read because according to https://golang.org/pkg/net/http/#Response // > The default HTTP client's Transport may not reuse HTTP/1.x "keep-alive" TCP connections // > if the Body is not read to completion and closed. _, err = io.Copy(ioutil.Discard, resp.Body) if err != nil { return e.errf("failed to read response body: %v", err) } if resp.StatusCode != http.StatusAccepted { return e.errf("failed to send spans to zipkin server with status %d", resp.StatusCode) } return nil } // Shutdown stops the exporter flushing any pending exports. func (e *Exporter) Shutdown(ctx context.Context) error { e.stoppedMu.Lock() e.stopped = true e.stoppedMu.Unlock() select { case <-ctx.Done(): return ctx.Err() default: } return nil } func (e *Exporter) logf(format string, args ...interface{}) { if e.logger != nil { e.logger.Printf(format, args...) } } func (e *Exporter) errf(format string, args ...interface{}) error { e.logf(format, args...) return fmt.Errorf(format, args...) } golang-opentelemetry-otel-1.1.0/exporters/zipkin/zipkin_test.go000066400000000000000000000216011414226744000250500ustar00rootroot00000000000000// 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 zipkin import ( "context" "encoding/json" "fmt" "io/ioutil" "log" "net" "net/http" "sync" "testing" "time" zkmodel "github.com/openzipkin/zipkin-go/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" ) const ( collectorURL = "http://localhost:9411/api/v2/spans" ) func TestNewRawExporter(t *testing.T) { _, err := New( collectorURL, ) assert.NoError(t, err) } func TestNewRawExporterShouldFailInvalidCollectorURL(t *testing.T) { var ( exp *Exporter err error ) // cannot be empty exp, err = New( "", ) assert.Error(t, err) assert.EqualError(t, err, "collector URL cannot be empty") assert.Nil(t, exp) // invalid URL exp, err = New( "localhost", ) assert.Error(t, err) assert.EqualError(t, err, "invalid collector URL \"localhost\": no scheme or host") assert.Nil(t, exp) } type mockZipkinCollector struct { t *testing.T url string closing bool server *http.Server wg *sync.WaitGroup lock sync.RWMutex models []zkmodel.SpanModel } func startMockZipkinCollector(t *testing.T) *mockZipkinCollector { collector := &mockZipkinCollector{ t: t, closing: false, } listener, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) collector.url = fmt.Sprintf("http://%s", listener.Addr().String()) server := &http.Server{ Handler: http.HandlerFunc(collector.handler), } collector.server = server wg := &sync.WaitGroup{} wg.Add(1) collector.wg = wg go func() { err := server.Serve(listener) require.True(t, collector.closing) require.Equal(t, http.ErrServerClosed, err) wg.Done() }() return collector } func (c *mockZipkinCollector) handler(w http.ResponseWriter, r *http.Request) { jsonBytes, err := ioutil.ReadAll(r.Body) require.NoError(c.t, err) var models []zkmodel.SpanModel err = json.Unmarshal(jsonBytes, &models) require.NoError(c.t, err) // for some reason we may get the nonUTC timestamps in models, // fix that for midx := range models { models[midx].Timestamp = models[midx].Timestamp.UTC() for aidx := range models[midx].Annotations { models[midx].Annotations[aidx].Timestamp = models[midx].Annotations[aidx].Timestamp.UTC() } } c.lock.Lock() defer c.lock.Unlock() c.models = append(c.models, models...) w.WriteHeader(http.StatusAccepted) } func (c *mockZipkinCollector) Close() { if c.closing { return } c.closing = true server := c.server c.server = nil require.NoError(c.t, server.Shutdown(context.Background())) c.wg.Wait() } func (c *mockZipkinCollector) ModelsLen() int { c.lock.RLock() defer c.lock.RUnlock() return len(c.models) } func (c *mockZipkinCollector) StealModels() []zkmodel.SpanModel { c.lock.Lock() defer c.lock.Unlock() models := c.models c.models = nil return models } type logStore struct { T *testing.T Messages []string } func (s *logStore) Write(p []byte) (n int, err error) { msg := (string)(p) if s.T != nil { s.T.Logf("%s", msg) } s.Messages = append(s.Messages, msg) return len(p), nil } func logStoreLogger(s *logStore) *log.Logger { return log.New(s, "", 0) } func TestExportSpans(t *testing.T) { resource := resource.NewSchemaless( semconv.ServiceNameKey.String("exporter-test"), ) spans := tracetest.SpanStubs{ // parent { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), SpanKind: trace.SpanKindServer, Name: "foo", StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), Attributes: nil, Events: nil, Status: sdktrace.Status{ Code: codes.Error, Description: "404, file not found", }, Resource: resource, }, // child { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xDF, 0xDE, 0xDD, 0xDC, 0xDB, 0xDA, 0xD9, 0xD8}, }), Parent: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, }), SpanKind: trace.SpanKindServer, Name: "bar", StartTime: time.Date(2020, time.March, 11, 19, 24, 15, 0, time.UTC), EndTime: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), Attributes: nil, Events: nil, Status: sdktrace.Status{ Code: codes.Error, Description: "403, forbidden", }, Resource: resource, }, }.Snapshots() models := []zkmodel.SpanModel{ // model of parent { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xfffefdfcfbfaf9f8), ParentID: nil, Debug: false, Sampled: nil, Err: nil, }, Name: "foo", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), Duration: time.Minute, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "exporter-test", }, RemoteEndpoint: nil, Annotations: nil, Tags: map[string]string{ "otel.status_code": "Error", "error": "404, file not found", }, }, // model of child { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ High: 0x001020304050607, Low: 0x8090a0b0c0d0e0f, }, ID: zkmodel.ID(0xdfdedddcdbdad9d8), ParentID: zkmodelIDPtr(0xfffefdfcfbfaf9f8), Debug: false, Sampled: nil, Err: nil, }, Name: "bar", Kind: "SERVER", Timestamp: time.Date(2020, time.March, 11, 19, 24, 15, 0, time.UTC), Duration: 30 * time.Second, Shared: false, LocalEndpoint: &zkmodel.Endpoint{ ServiceName: "exporter-test", }, RemoteEndpoint: nil, Annotations: nil, Tags: map[string]string{ "otel.status_code": "Error", "error": "403, forbidden", }, }, } require.Len(t, models, len(spans)) collector := startMockZipkinCollector(t) defer collector.Close() ls := &logStore{T: t} logger := logStoreLogger(ls) exporter, err := New(collector.url, WithLogger(logger)) require.NoError(t, err) ctx := context.Background() require.Len(t, ls.Messages, 0) require.NoError(t, exporter.ExportSpans(ctx, spans[0:1])) require.Len(t, ls.Messages, 1) require.Contains(t, ls.Messages[0], "send a POST request") ls.Messages = nil require.NoError(t, exporter.ExportSpans(ctx, nil)) require.Len(t, ls.Messages, 1) require.Contains(t, ls.Messages[0], "no spans to export") ls.Messages = nil require.NoError(t, exporter.ExportSpans(ctx, spans[1:2])) require.Contains(t, ls.Messages[0], "send a POST request") checkFunc := func() bool { return collector.ModelsLen() == len(models) } require.Eventually(t, checkFunc, time.Second, 10*time.Millisecond) require.Equal(t, models, collector.StealModels()) } func TestExporterShutdownHonorsTimeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() exp, err := New(collectorURL) require.NoError(t, err) innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond) defer innerCancel() <-innerCtx.Done() assert.Errorf(t, exp.Shutdown(innerCtx), context.DeadlineExceeded.Error()) } func TestExporterShutdownHonorsCancel(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() exp, err := New(collectorURL) require.NoError(t, err) innerCtx, innerCancel := context.WithCancel(ctx) innerCancel() assert.Errorf(t, exp.Shutdown(innerCtx), context.Canceled.Error()) } func TestErrorOnExportShutdownExporter(t *testing.T) { exp, err := New(collectorURL) require.NoError(t, err) assert.NoError(t, exp.Shutdown(context.Background())) assert.NoError(t, exp.ExportSpans(context.Background(), nil)) } golang-opentelemetry-otel-1.1.0/get_main_pkgs.sh000077500000000000000000000023361414226744000217710ustar00rootroot00000000000000#!/usr/bin/env bash # 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. set -euo pipefail top_dir='.' if [[ $# -gt 0 ]]; then top_dir="${1}" fi p=$(pwd) mod_dirs=() # Note `mapfile` does not exist in older bash versions: # https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash while IFS= read -r line; do mod_dirs+=("$line") done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort) for mod_dir in "${mod_dirs[@]}"; do cd "${mod_dir}" while IFS= read -r line; do echo ".${line#${p}}" done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|') cd "${p}" done golang-opentelemetry-otel-1.1.0/go.mod000066400000000000000000000051711414226744000177310ustar00rootroot00000000000000module go.opentelemetry.io/otel go 1.15 require ( github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel/trace v1.1.0 ) replace go.opentelemetry.io/otel => ./ replace go.opentelemetry.io/otel/bridge/opencensus => ./bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ./bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ./example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ./example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ./example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ./example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ./example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ./example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ./example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ./exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ./exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ./exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ./internal/tools replace go.opentelemetry.io/otel/sdk => ./sdk replace go.opentelemetry.io/otel/internal/metric => ./internal/metric replace go.opentelemetry.io/otel/metric => ./metric replace go.opentelemetry.io/otel/sdk/export/metric => ./sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ./sdk/metric replace go.opentelemetry.io/otel/trace => ./trace replace go.opentelemetry.io/otel/example/passthrough => ./example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ./exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ./exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ./exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ./exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ./exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ./exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ./exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ./bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ./example/fib replace go.opentelemetry.io/otel/schema => ./schema golang-opentelemetry-otel-1.1.0/go.sum000066400000000000000000000025761414226744000177640ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/handler.go000066400000000000000000000064051414226744000205700ustar00rootroot00000000000000// 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 otel // import "go.opentelemetry.io/otel" import ( "log" "os" "sync" "sync/atomic" ) var ( // globalErrorHandler provides an ErrorHandler that can be used // throughout an OpenTelemetry instrumented project. When a user // specified ErrorHandler is registered (`SetErrorHandler`) all calls to // `Handle` and will be delegated to the registered ErrorHandler. globalErrorHandler = defaultErrorHandler() // delegateErrorHandlerOnce ensures that a user provided ErrorHandler is // only ever registered once. delegateErrorHandlerOnce sync.Once // Compile-time check that delegator implements ErrorHandler. _ ErrorHandler = (*delegator)(nil) ) type holder struct { eh ErrorHandler } func defaultErrorHandler() *atomic.Value { v := &atomic.Value{} v.Store(holder{eh: &delegator{l: log.New(os.Stderr, "", log.LstdFlags)}}) return v } // delegator logs errors if no delegate is set, otherwise they are delegated. type delegator struct { delegate atomic.Value l *log.Logger } // setDelegate sets the ErrorHandler delegate. func (h *delegator) setDelegate(d ErrorHandler) { // It is critical this is guarded with delegateErrorHandlerOnce, if it is // called again with a different concrete type it will panic. h.delegate.Store(d) } // Handle logs err if no delegate is set, otherwise it is delegated. func (h *delegator) Handle(err error) { if d := h.delegate.Load(); d != nil { d.(ErrorHandler).Handle(err) return } h.l.Print(err) } // GetErrorHandler returns the global ErrorHandler instance. // // The default ErrorHandler instance returned will log all errors to STDERR // until an override ErrorHandler is set with SetErrorHandler. All // ErrorHandler returned prior to this will automatically forward errors to // the set instance instead of logging. // // Subsequent calls to SetErrorHandler after the first will not forward errors // to the new ErrorHandler for prior returned instances. func GetErrorHandler() ErrorHandler { return globalErrorHandler.Load().(holder).eh } // SetErrorHandler sets the global ErrorHandler to h. // // The first time this is called all ErrorHandler previously returned from // GetErrorHandler will send errors to h instead of the default logging // ErrorHandler. Subsequent calls will set the global ErrorHandler, but not // delegate errors to h. func SetErrorHandler(h ErrorHandler) { delegateErrorHandlerOnce.Do(func() { current := GetErrorHandler() if current == h { return } if internalHandler, ok := current.(*delegator); ok { internalHandler.setDelegate(h) } }) globalErrorHandler.Store(holder{eh: h}) } // Handle is a convenience function for ErrorHandler().Handle(err) func Handle(err error) { GetErrorHandler().Handle(err) } golang-opentelemetry-otel-1.1.0/handler_test.go000066400000000000000000000135541414226744000216320ustar00rootroot00000000000000// 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 otel import ( "bytes" "errors" "io/ioutil" "log" "sync" "sync/atomic" "testing" "github.com/stretchr/testify/suite" ) type errLogger []string func (l *errLogger) Write(p []byte) (int, error) { msg := bytes.TrimRight(p, "\n") (*l) = append(*l, string(msg)) return len(msg), nil } func (l *errLogger) Reset() { *l = errLogger([]string{}) } func (l *errLogger) Got() []string { return []string(*l) } type logger struct { l *log.Logger } func (l *logger) Handle(err error) { l.l.Print(err) } func causeErr(text string) { Handle(errors.New(text)) } type HandlerTestSuite struct { suite.Suite origHandler ErrorHandler errLogger *errLogger } func (s *HandlerTestSuite) SetupSuite() { s.errLogger = new(errLogger) s.origHandler = globalErrorHandler.Load().(holder).eh globalErrorHandler.Store(holder{eh: &delegator{l: log.New(s.errLogger, "", 0)}}) } func (s *HandlerTestSuite) TearDownSuite() { globalErrorHandler.Store(holder{eh: s.origHandler}) delegateErrorHandlerOnce = sync.Once{} } func (s *HandlerTestSuite) SetupTest() { s.errLogger.Reset() } func (s *HandlerTestSuite) TearDownTest() { globalErrorHandler.Store(holder{eh: &delegator{l: log.New(s.errLogger, "", 0)}}) delegateErrorHandlerOnce = sync.Once{} } func (s *HandlerTestSuite) TestGlobalHandler() { errs := []string{"one", "two"} GetErrorHandler().Handle(errors.New(errs[0])) Handle(errors.New(errs[1])) s.Assert().Equal(errs, s.errLogger.Got()) } func (s *HandlerTestSuite) TestDelegatedHandler() { eh := GetErrorHandler() newErrLogger := new(errLogger) SetErrorHandler(&logger{l: log.New(newErrLogger, "", 0)}) errs := []string{"TestDelegatedHandler"} eh.Handle(errors.New(errs[0])) s.Assert().Equal(errs, newErrLogger.Got()) } func (s *HandlerTestSuite) TestSettingDefaultIsANoOp() { SetErrorHandler(GetErrorHandler()) d := globalErrorHandler.Load().(holder).eh.(*delegator) s.Assert().Nil(d.delegate.Load()) } func (s *HandlerTestSuite) TestNoDropsOnDelegate() { causeErr("") s.Require().Len(s.errLogger.Got(), 1) // Change to another Handler. We are testing this is loss-less. newErrLogger := new(errLogger) secondary := &logger{ l: log.New(newErrLogger, "", 0), } SetErrorHandler(secondary) causeErr("") s.Assert().Len(s.errLogger.Got(), 1, "original Handler used after delegation") s.Assert().Len(newErrLogger.Got(), 1, "new Handler not used after delegation") } func (s *HandlerTestSuite) TestAllowMultipleSets() { notUsed := new(errLogger) secondary := &logger{l: log.New(notUsed, "", 0)} SetErrorHandler(secondary) s.Require().Same(GetErrorHandler(), secondary, "new Handler not set") tertiary := &logger{l: log.New(notUsed, "", 0)} SetErrorHandler(tertiary) s.Assert().Same(GetErrorHandler(), tertiary, "user Handler not overridden") } func TestHandlerTestSuite(t *testing.T) { suite.Run(t, new(HandlerTestSuite)) } func BenchmarkErrorHandler(b *testing.B) { primary := &delegator{l: log.New(ioutil.Discard, "", 0)} secondary := &logger{l: log.New(ioutil.Discard, "", 0)} tertiary := &logger{l: log.New(ioutil.Discard, "", 0)} globalErrorHandler.Store(holder{eh: primary}) err := errors.New("BenchmarkErrorHandler") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { GetErrorHandler().Handle(err) Handle(err) SetErrorHandler(secondary) GetErrorHandler().Handle(err) Handle(err) SetErrorHandler(tertiary) GetErrorHandler().Handle(err) Handle(err) b.StopTimer() primary.delegate = atomic.Value{} globalErrorHandler.Store(holder{eh: primary}) delegateErrorHandlerOnce = sync.Once{} b.StartTimer() } b.StopTimer() reset() } var eh ErrorHandler func BenchmarkGetDefaultErrorHandler(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { eh = GetErrorHandler() } } func BenchmarkGetDelegatedErrorHandler(b *testing.B) { SetErrorHandler(&logger{l: log.New(ioutil.Discard, "", 0)}) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { eh = GetErrorHandler() } b.StopTimer() reset() } func BenchmarkDefaultErrorHandlerHandle(b *testing.B) { globalErrorHandler.Store(holder{ eh: &delegator{l: log.New(ioutil.Discard, "", 0)}, }) eh := GetErrorHandler() err := errors.New("BenchmarkDefaultErrorHandlerHandle") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { eh.Handle(err) } b.StopTimer() reset() } func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) { eh := GetErrorHandler() SetErrorHandler(&logger{l: log.New(ioutil.Discard, "", 0)}) err := errors.New("BenchmarkDelegatedErrorHandlerHandle") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { eh.Handle(err) } b.StopTimer() reset() } func BenchmarkSetErrorHandlerDelegation(b *testing.B) { alt := &logger{l: log.New(ioutil.Discard, "", 0)} b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { SetErrorHandler(alt) b.StopTimer() reset() b.StartTimer() } } func BenchmarkSetErrorHandlerNoDelegation(b *testing.B) { eh := []ErrorHandler{ &logger{l: log.New(ioutil.Discard, "", 0)}, &logger{l: log.New(ioutil.Discard, "", 0)}, } mod := len(eh) // Do not measure delegation. SetErrorHandler(eh[1]) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { SetErrorHandler(eh[i%mod]) } b.StopTimer() reset() } func reset() { globalErrorHandler = defaultErrorHandler() delegateErrorHandlerOnce = sync.Once{} } golang-opentelemetry-otel-1.1.0/internal/000077500000000000000000000000001414226744000204335ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/baggage/000077500000000000000000000000001414226744000220105ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/baggage/baggage.go000066400000000000000000000031221414226744000237120ustar00rootroot00000000000000// 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 baggage provides base types and functionality to store and retrieve baggage in Go context. This package exists because the OpenTracing bridge to OpenTelemetry needs to synchronize state whenever baggage for a context is modified and that context contains an OpenTracing span. If it were not for this need this package would not need to exist and the `go.opentelemetry.io/otel/baggage` package would be the singular place where W3C baggage is handled. */ package baggage // List is the collection of baggage members. The W3C allows for duplicates, // but OpenTelemetry does not, therefore, this is represented as a map. type List map[string]Item // Item is the value and metadata properties part of a list-member. type Item struct { Value string Properties []Property } // Property is a metadata entry for a list-member. type Property struct { Key, Value string // HasValue indicates if a zero-value value means the property does not // have a value or if it was the zero-value. HasValue bool } golang-opentelemetry-otel-1.1.0/internal/baggage/context.go000066400000000000000000000051401414226744000240230ustar00rootroot00000000000000// 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 baggage import "context" type baggageContextKeyType int const baggageKey baggageContextKeyType = iota // SetHookFunc is a callback called when storing baggage in the context. type SetHookFunc func(context.Context, List) context.Context // GetHookFunc is a callback called when getting baggage from the context. type GetHookFunc func(context.Context, List) List type baggageState struct { list List setHook SetHookFunc getHook GetHookFunc } // ContextWithSetHook returns a copy of parent with hook configured to be // invoked every time ContextWithBaggage is called. // // Passing nil SetHookFunc creates a context with no set hook to call. func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context { var s baggageState switch v := parent.Value(baggageKey).(type) { case baggageState: s = v } s.setHook = hook return context.WithValue(parent, baggageKey, s) } // ContextWithGetHook returns a copy of parent with hook configured to be // invoked every time FromContext is called. // // Passing nil GetHookFunc creates a context with no get hook to call. func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context { var s baggageState switch v := parent.Value(baggageKey).(type) { case baggageState: s = v } s.getHook = hook return context.WithValue(parent, baggageKey, s) } // ContextWithList returns a copy of parent with baggage. Passing nil list // returns a context without any baggage. func ContextWithList(parent context.Context, list List) context.Context { var s baggageState switch v := parent.Value(baggageKey).(type) { case baggageState: s = v } s.list = list ctx := context.WithValue(parent, baggageKey, s) if s.setHook != nil { ctx = s.setHook(ctx, list) } return ctx } // ListFromContext returns the baggage contained in ctx. func ListFromContext(ctx context.Context) List { switch v := ctx.Value(baggageKey).(type) { case baggageState: if v.getHook != nil { return v.getHook(ctx, v.list) } return v.list default: return nil } } golang-opentelemetry-otel-1.1.0/internal/baggage/context_test.go000066400000000000000000000054301414226744000250640ustar00rootroot00000000000000// 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 baggage import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestContextWithList(t *testing.T) { ctx := context.Background() l := List{"foo": {Value: "1"}} nCtx := ContextWithList(ctx, l) assert.Equal(t, baggageState{list: l}, nCtx.Value(baggageKey)) assert.Nil(t, ctx.Value(baggageKey)) } func TestClearContextOfList(t *testing.T) { l := List{"foo": {Value: "1"}} ctx := context.Background() ctx = context.WithValue(ctx, baggageKey, l) nCtx := ContextWithList(ctx, nil) nL, ok := nCtx.Value(baggageKey).(baggageState) require.True(t, ok, "wrong type stored in context") assert.Nil(t, nL.list) assert.Equal(t, l, ctx.Value(baggageKey)) } func TestListFromContext(t *testing.T) { ctx := context.Background() assert.Nil(t, ListFromContext(ctx)) l := List{"foo": {Value: "1"}} ctx = context.WithValue(ctx, baggageKey, baggageState{list: l}) assert.Equal(t, l, ListFromContext(ctx)) } func TestContextWithSetHook(t *testing.T) { var called bool f := func(ctx context.Context, list List) context.Context { called = true return ctx } ctx := context.Background() ctx = ContextWithSetHook(ctx, f) assert.False(t, called, "SetHookFunc called when setting hook") ctx = ContextWithList(ctx, nil) assert.True(t, called, "SetHookFunc not called when setting List") // Ensure resetting the hook works. called = false ctx = ContextWithSetHook(ctx, f) assert.False(t, called, "SetHookFunc called when re-setting hook") ContextWithList(ctx, nil) assert.True(t, called, "SetHookFunc not called when re-setting List") } func TestContextWithGetHook(t *testing.T) { var called bool f := func(ctx context.Context, list List) List { called = true return list } ctx := context.Background() ctx = ContextWithGetHook(ctx, f) assert.False(t, called, "GetHookFunc called when setting hook") _ = ListFromContext(ctx) assert.True(t, called, "GetHookFunc not called when getting List") // Ensure resetting the hook works. called = false ctx = ContextWithGetHook(ctx, f) assert.False(t, called, "GetHookFunc called when re-setting hook") _ = ListFromContext(ctx) assert.True(t, called, "GetHookFunc not called when re-getting List") } golang-opentelemetry-otel-1.1.0/internal/global/000077500000000000000000000000001414226744000216735ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/global/benchmark_test.go000066400000000000000000000020311414226744000252070ustar00rootroot00000000000000// 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_test import ( "context" "testing" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" ) func BenchmarkStartEndSpanNoSDK(b *testing.B) { // Compare with BenchmarkStartEndSpan() in // ../../sdk/trace/benchmark_test.go. global.ResetForTest() t := otel.Tracer("Benchmark StartEndSpan") ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.End() } } golang-opentelemetry-otel-1.1.0/internal/global/propagator.go000066400000000000000000000052211414226744000244000ustar00rootroot00000000000000// 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 ( "context" "sync" "go.opentelemetry.io/otel/propagation" ) // textMapPropagator is a default TextMapPropagator that delegates calls to a // registered delegate if one is set, otherwise it defaults to delegating the // calls to a the default no-op propagation.TextMapPropagator. type textMapPropagator struct { mtx sync.Mutex once sync.Once delegate propagation.TextMapPropagator noop propagation.TextMapPropagator } // Compile-time guarantee that textMapPropagator implements the // propagation.TextMapPropagator interface. var _ propagation.TextMapPropagator = (*textMapPropagator)(nil) func newTextMapPropagator() *textMapPropagator { return &textMapPropagator{ noop: propagation.NewCompositeTextMapPropagator(), } } // SetDelegate sets a delegate propagation.TextMapPropagator that all calls are // forwarded to. Delegation can only be performed once, all subsequent calls // perform no delegation. func (p *textMapPropagator) SetDelegate(delegate propagation.TextMapPropagator) { if delegate == nil { return } p.mtx.Lock() p.once.Do(func() { p.delegate = delegate }) p.mtx.Unlock() } // effectiveDelegate returns the current delegate of p if one is set, // otherwise the default noop TextMapPropagator is returned. This method // can be called concurrently. func (p *textMapPropagator) effectiveDelegate() propagation.TextMapPropagator { p.mtx.Lock() defer p.mtx.Unlock() if p.delegate != nil { return p.delegate } return p.noop } // Inject set cross-cutting concerns from the Context into the carrier. func (p *textMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { p.effectiveDelegate().Inject(ctx, carrier) } // Extract reads cross-cutting concerns from the carrier into a Context. func (p *textMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { return p.effectiveDelegate().Extract(ctx, carrier) } // Fields returns the keys whose values are set with Inject. func (p *textMapPropagator) Fields() []string { return p.effectiveDelegate().Fields() } golang-opentelemetry-otel-1.1.0/internal/global/propagator_test.go000066400000000000000000000062471414226744000254500ustar00rootroot00000000000000// 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_test import ( "context" "testing" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/internal/internaltest" ) func TestTextMapPropagatorDelegation(t *testing.T) { global.ResetForTest() ctx := context.Background() carrier := internaltest.NewTextMapCarrier(nil) // The default should be a noop. initial := global.TextMapPropagator() initial.Inject(ctx, carrier) ctx = initial.Extract(ctx, carrier) if !carrier.GotN(t, 0) || !carrier.SetN(t, 0) { return } // Make sure the delegate woks as expected. delegate := internaltest.NewTextMapPropagator("test") delegate.Inject(ctx, carrier) ctx = delegate.Extract(ctx, carrier) if !delegate.InjectedN(t, carrier, 1) || !delegate.ExtractedN(t, ctx, 1) { return } // The initial propagator should use the delegate after it is set as the // global. global.SetTextMapPropagator(delegate) initial.Inject(ctx, carrier) ctx = initial.Extract(ctx, carrier) delegate.InjectedN(t, carrier, 2) delegate.ExtractedN(t, ctx, 2) } func TestTextMapPropagatorDelegationNil(t *testing.T) { global.ResetForTest() ctx := context.Background() carrier := internaltest.NewTextMapCarrier(nil) // The default should be a noop. initial := global.TextMapPropagator() initial.Inject(ctx, carrier) ctx = initial.Extract(ctx, carrier) if !carrier.GotN(t, 0) || !carrier.SetN(t, 0) { return } // Delegation to nil should not make a change. global.SetTextMapPropagator(nil) initial.Inject(ctx, carrier) initial.Extract(ctx, carrier) if !carrier.GotN(t, 0) || !carrier.SetN(t, 0) { return } } func TestTextMapPropagatorFields(t *testing.T) { global.ResetForTest() initial := global.TextMapPropagator() delegate := internaltest.NewTextMapPropagator("test") delegateFields := delegate.Fields() // Sanity check on the initial Fields. if got := initial.Fields(); fieldsEqual(got, delegateFields) { t.Fatalf("testing fields (%v) matched Noop fields (%v)", delegateFields, got) } global.SetTextMapPropagator(delegate) // Check previous returns from global not correctly delegate. if got := initial.Fields(); !fieldsEqual(got, delegateFields) { t.Errorf("global TextMapPropagator.Fields returned %v instead of delegating, want (%v)", got, delegateFields) } // Check new calls to global. if got := global.TextMapPropagator().Fields(); !fieldsEqual(got, delegateFields) { t.Errorf("global TextMapPropagator.Fields returned %v, want (%v)", got, delegateFields) } } func fieldsEqual(f1, f2 []string) bool { if len(f1) != len(f2) { return false } for i := range f1 { if f1[i] != f2[i] { return false } } return true } golang-opentelemetry-otel-1.1.0/internal/global/state.go000066400000000000000000000063641414226744000233530ustar00rootroot00000000000000// 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 ( "sync" "sync/atomic" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) type ( tracerProviderHolder struct { tp trace.TracerProvider } propagatorsHolder struct { tm propagation.TextMapPropagator } ) var ( globalTracer = defaultTracerValue() globalPropagators = defaultPropagatorsValue() delegateTraceOnce sync.Once delegateTextMapPropagatorOnce sync.Once ) // TracerProvider is the internal implementation for global.TracerProvider. func TracerProvider() trace.TracerProvider { return globalTracer.Load().(tracerProviderHolder).tp } // SetTracerProvider is the internal implementation for global.SetTracerProvider. func SetTracerProvider(tp trace.TracerProvider) { delegateTraceOnce.Do(func() { current := TracerProvider() if current == tp { // Setting the provider to the prior default is nonsense, panic. // Panic is acceptable because we are likely still early in the // process lifetime. panic("invalid TracerProvider, the global instance cannot be reinstalled") } else if def, ok := current.(*tracerProvider); ok { def.setDelegate(tp) } }) globalTracer.Store(tracerProviderHolder{tp: tp}) } // TextMapPropagator is the internal implementation for global.TextMapPropagator. func TextMapPropagator() propagation.TextMapPropagator { return globalPropagators.Load().(propagatorsHolder).tm } // SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator. func SetTextMapPropagator(p propagation.TextMapPropagator) { // For the textMapPropagator already returned by TextMapPropagator // delegate to p. delegateTextMapPropagatorOnce.Do(func() { if current := TextMapPropagator(); current == p { // Setting the provider to the prior default is nonsense, panic. // Panic is acceptable because we are likely still early in the // process lifetime. panic("invalid TextMapPropagator, the global instance cannot be reinstalled") } else if def, ok := current.(*textMapPropagator); ok { def.SetDelegate(p) } }) // Return p when subsequent calls to TextMapPropagator are made. globalPropagators.Store(propagatorsHolder{tm: p}) } func defaultTracerValue() *atomic.Value { v := &atomic.Value{} v.Store(tracerProviderHolder{tp: &tracerProvider{}}) return v } func defaultPropagatorsValue() *atomic.Value { v := &atomic.Value{} v.Store(propagatorsHolder{tm: newTextMapPropagator()}) return v } // ResetForTest restores the initial global state, for testing purposes. func ResetForTest() { globalTracer = defaultTracerValue() globalPropagators = defaultPropagatorsValue() delegateTraceOnce = sync.Once{} delegateTextMapPropagatorOnce = sync.Once{} } golang-opentelemetry-otel-1.1.0/internal/global/state_test.go000066400000000000000000000023211414226744000243770ustar00rootroot00000000000000// 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_test import ( "testing" "go.opentelemetry.io/otel/internal/global" ) func TestResetsOfGlobalsPanic(t *testing.T) { global.ResetForTest() tests := map[string]func(){ "SetTextMapPropagator": func() { global.SetTextMapPropagator(global.TextMapPropagator()) }, "SetTracerProvider": func() { global.SetTracerProvider(global.TracerProvider()) }, } for name, test := range tests { shouldPanic(t, name, test) } } func shouldPanic(t *testing.T, name string, f func()) { defer func() { if r := recover(); r == nil { t.Errorf("calling %s with default global did not panic", name) } }() f() } golang-opentelemetry-otel-1.1.0/internal/global/trace.go000066400000000000000000000130201414226744000233140ustar00rootroot00000000000000// 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 /* This file contains the forwarding implementation of the TracerProvider used as the default global instance. Prior to initialization of an SDK, Tracers returned by the global TracerProvider will provide no-op functionality. This means that all Span created prior to initialization are no-op Spans. Once an SDK has been initialized, all provided no-op Tracers are swapped for Tracers provided by the SDK defined TracerProvider. However, any Span started prior to this initialization does not change its behavior. Meaning, the Span remains a no-op Span. The implementation to track and swap Tracers locks all new Tracer creation until the swap is complete. This assumes that this operation is not performance-critical. If that assumption is incorrect, be sure to configure an SDK prior to any Tracer creation. */ import ( "context" "sync" "sync/atomic" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) // tracerProvider is a placeholder for a configured SDK TracerProvider. // // All TracerProvider functionality is forwarded to a delegate once // configured. type tracerProvider struct { mtx sync.Mutex tracers map[il]*tracer delegate trace.TracerProvider } // Compile-time guarantee that tracerProvider implements the TracerProvider // interface. var _ trace.TracerProvider = &tracerProvider{} // setDelegate configures p to delegate all TracerProvider functionality to // provider. // // All Tracers provided prior to this function call are switched out to be // Tracers provided by provider. // // It is guaranteed by the caller that this happens only once. func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { p.mtx.Lock() defer p.mtx.Unlock() p.delegate = provider if len(p.tracers) == 0 { return } for _, t := range p.tracers { t.setDelegate(provider) } p.tracers = nil } // Tracer implements TracerProvider. func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { p.mtx.Lock() defer p.mtx.Unlock() if p.delegate != nil { return p.delegate.Tracer(name, opts...) } // At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map. c := trace.NewTracerConfig(opts...) key := il{ name: name, version: c.InstrumentationVersion(), } if p.tracers == nil { p.tracers = make(map[il]*tracer) } if val, ok := p.tracers[key]; ok { return val } t := &tracer{name: name, opts: opts, provider: p} p.tracers[key] = t return t } type il struct { name string version string } // tracer is a placeholder for a trace.Tracer. // // All Tracer functionality is forwarded to a delegate once configured. // Otherwise, all functionality is forwarded to a NoopTracer. type tracer struct { name string opts []trace.TracerOption provider *tracerProvider delegate atomic.Value } // Compile-time guarantee that tracer implements the trace.Tracer interface. var _ trace.Tracer = &tracer{} // setDelegate configures t to delegate all Tracer functionality to Tracers // created by provider. // // All subsequent calls to the Tracer methods will be passed to the delegate. // // It is guaranteed by the caller that this happens only once. func (t *tracer) setDelegate(provider trace.TracerProvider) { t.delegate.Store(provider.Tracer(t.name, t.opts...)) } // Start implements trace.Tracer by forwarding the call to t.delegate if // set, otherwise it forwards the call to a NoopTracer. func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { delegate := t.delegate.Load() if delegate != nil { return delegate.(trace.Tracer).Start(ctx, name, opts...) } s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t} ctx = trace.ContextWithSpan(ctx, s) return ctx, s } // nonRecordingSpan is a minimal implementation of a Span that wraps a // SpanContext. It performs no operations other than to return the wrapped // SpanContext. type nonRecordingSpan struct { sc trace.SpanContext tracer *tracer } var _ trace.Span = nonRecordingSpan{} // SpanContext returns the wrapped SpanContext. func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc } // IsRecording always returns false. func (nonRecordingSpan) IsRecording() bool { return false } // SetStatus does nothing. func (nonRecordingSpan) SetStatus(codes.Code, string) {} // SetError does nothing. func (nonRecordingSpan) SetError(bool) {} // SetAttributes does nothing. func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} // End does nothing. func (nonRecordingSpan) End(...trace.SpanEndOption) {} // RecordError does nothing. func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} // AddEvent does nothing. func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {} // SetName does nothing. func (nonRecordingSpan) SetName(string) {} func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } golang-opentelemetry-otel-1.1.0/internal/global/trace_test.go000066400000000000000000000157531414226744000243720ustar00rootroot00000000000000// 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_test import ( "context" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/trace" ) type fnTracerProvider struct { tracer func(string, ...trace.TracerOption) trace.Tracer } func (fn fnTracerProvider) Tracer(instrumentationName string, opts ...trace.TracerOption) trace.Tracer { return fn.tracer(instrumentationName, opts...) } type fnTracer struct { start func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) } func (fn fnTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { return fn.start(ctx, spanName, opts...) } func TestTraceProviderDelegation(t *testing.T) { global.ResetForTest() // Map of tracers to expected span names. expected := map[string][]string{ "pre": {"span2"}, "post": {"span3"}, "fromSpan": {"span4"}, } ctx := context.Background() gtp := otel.GetTracerProvider() tracer1 := gtp.Tracer("pre") // This is started before an SDK was registered and should be dropped. _, span1 := tracer1.Start(ctx, "span1") otel.SetTracerProvider(fnTracerProvider{ tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { spans, ok := expected[name] assert.Truef(t, ok, "invalid tracer: %s", name) return fnTracer{ start: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { if ok { if len(spans) == 0 { t.Errorf("unexpected span: %s", spanName) } else { var want string want, spans = spans[0], spans[1:] assert.Equal(t, want, spanName) } } return trace.NewNoopTracerProvider().Tracer(name).Start(ctx, spanName) }, } }, }) // This span was started before initialization, it is expected to be dropped. span1.End() // The existing Tracer should have been configured to now use the configured SDK. _, span2 := tracer1.Start(ctx, "span2") span2.End() // The global TracerProvider should now create Tracers that also use the newly configured SDK. tracer2 := gtp.Tracer("post") _, span3 := tracer2.Start(ctx, "span3") span3.End() // The noop-span should still provide access to a usable TracerProvider. _, span4 := span1.TracerProvider().Tracer("fromSpan").Start(ctx, "span4") span4.End() } func TestTraceProviderDelegates(t *testing.T) { global.ResetForTest() // Retrieve the placeholder TracerProvider. gtp := otel.GetTracerProvider() // Configure it with a spy. called := false otel.SetTracerProvider(fnTracerProvider{ tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { called = true assert.Equal(t, "abc", name) return trace.NewNoopTracerProvider().Tracer("") }, }) gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")) assert.True(t, called, "expected configured TraceProvider to be called") } func TestTraceProviderDelegatesConcurrentSafe(t *testing.T) { global.ResetForTest() // Retrieve the placeholder TracerProvider. gtp := otel.GetTracerProvider() done := make(chan struct{}) quit := make(chan struct{}) go func() { defer close(done) for { select { case <-time.After(1 * time.Millisecond): gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")) case <-quit: return } } }() // Wait for the goroutine to make some calls before installing the provider. <-time.After(100 * time.Millisecond) // Configure it with a spy. called := int32(0) otel.SetTracerProvider(fnTracerProvider{ tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { newVal := atomic.AddInt32(&called, 1) assert.Equal(t, "abc", name) if newVal == 10 { // Signal the goroutine to finish. close(quit) } return trace.NewNoopTracerProvider().Tracer("") }, }) // Wait for the go routine to finish <-done assert.LessOrEqual(t, int32(10), atomic.LoadInt32(&called), "expected configured TraceProvider to be called") } func TestTracerDelegatesConcurrentSafe(t *testing.T) { global.ResetForTest() // Retrieve the placeholder TracerProvider. gtp := otel.GetTracerProvider() tracer := gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")) done := make(chan struct{}) quit := make(chan struct{}) go func() { defer close(done) for { select { case <-time.After(1 * time.Millisecond): tracer.Start(context.Background(), "name") case <-quit: return } } }() // Wait for the goroutine to make some calls before installing the provider. <-time.After(100 * time.Millisecond) // Configure it with a spy. called := int32(0) otel.SetTracerProvider(fnTracerProvider{ tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { assert.Equal(t, "abc", name) return fnTracer{ start: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { newVal := atomic.AddInt32(&called, 1) assert.Equal(t, "name", spanName) if newVal == 10 { // Signal the goroutine to finish. close(quit) } return trace.NewNoopTracerProvider().Tracer("").Start(ctx, spanName) }, } }, }) // Wait for the go routine to finish <-done assert.LessOrEqual(t, int32(10), atomic.LoadInt32(&called), "expected configured TraceProvider to be called") } func TestTraceProviderDelegatesSameInstance(t *testing.T) { global.ResetForTest() // Retrieve the placeholder TracerProvider. gtp := otel.GetTracerProvider() tracer := gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")) assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) otel.SetTracerProvider(fnTracerProvider{ tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { return trace.NewNoopTracerProvider().Tracer("") }, }) assert.NotSame(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) } func TestSpanContextPropagatedWithNonRecordingSpan(t *testing.T) { global.ResetForTest() sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: [16]byte{0x01}, SpanID: [8]byte{0x01}, TraceFlags: trace.FlagsSampled, Remote: true, }) ctx := trace.ContextWithSpanContext(context.Background(), sc) _, span := otel.Tracer("test").Start(ctx, "test") assert.Equal(t, sc, span.SpanContext()) assert.False(t, span.IsRecording()) } golang-opentelemetry-otel-1.1.0/internal/internaltest/000077500000000000000000000000001414226744000231475ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/internaltest/alignment.go000066400000000000000000000042551414226744000254620ustar00rootroot00000000000000// 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 internaltest /* This file contains common utilities and objects to validate memory alignment of Go types. The primary use of this functionality is intended to ensure `struct` fields that need to be 64-bit aligned so they can be passed as arguments to 64-bit atomic operations. The common workflow is to define a slice of `FieldOffset` and pass them to the `Aligned8Byte` function from within a `TestMain` function from a package's tests. It is important to make this call from the `TestMain` function prior to running the rest of the test suit as it can provide useful diagnostics about field alignment instead of ambiguous nil pointer dereference and runtime panic. For more information: https://github.com/open-telemetry/opentelemetry-go/issues/341 */ import ( "fmt" "io" ) // FieldOffset is a preprocessor representation of a struct field alignment. type FieldOffset struct { // Name of the field. Name string // Offset of the field in bytes. // // To compute this at compile time use unsafe.Offsetof. Offset uintptr } // Aligned8Byte returns if all fields are aligned modulo 8-bytes. // // Error messaging is printed to out for any field determined misaligned. func Aligned8Byte(fields []FieldOffset, out io.Writer) bool { misaligned := make([]FieldOffset, 0) for _, f := range fields { if f.Offset%8 != 0 { misaligned = append(misaligned, f) } } if len(misaligned) == 0 { return true } fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:") for _, f := range misaligned { fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset) } return false } golang-opentelemetry-otel-1.1.0/internal/internaltest/env.go000066400000000000000000000036621414226744000242750ustar00rootroot00000000000000// 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 internaltest import ( "os" ) type Env struct { Name string Value string Exists bool } // EnvStore stores and recovers environment variables. type EnvStore interface { // Records the environment variable into the store. Record(key string) // Restore recovers the environment variables in the store. Restore() error } var _ EnvStore = (*envStore)(nil) type envStore struct { store map[string]Env } func (s *envStore) add(env Env) { s.store[env.Name] = env } func (s *envStore) Restore() error { var err error for _, v := range s.store { if v.Exists { err = os.Setenv(v.Name, v.Value) } else { err = os.Unsetenv(v.Name) } if err != nil { return err } } return nil } func (s *envStore) setEnv(key, value string) error { s.Record(key) err := os.Setenv(key, value) if err != nil { return err } return nil } func (s *envStore) Record(key string) { originValue, exists := os.LookupEnv(key) s.add(Env{ Name: key, Value: originValue, Exists: exists, }) } func NewEnvStore() EnvStore { return newEnvStore() } func newEnvStore() *envStore { return &envStore{store: make(map[string]Env)} } func SetEnvVariables(env map[string]string) (EnvStore, error) { envStore := newEnvStore() for k, v := range env { err := envStore.setEnv(k, v) if err != nil { return nil, err } } return envStore, nil } golang-opentelemetry-otel-1.1.0/internal/internaltest/env_test.go000066400000000000000000000113271414226744000253310ustar00rootroot00000000000000// 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 internaltest import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) type EnvStoreTestSuite struct { suite.Suite } func (s *EnvStoreTestSuite) Test_add() { envStore := newEnvStore() e := Env{ Name: "name", Value: "value", Exists: true, } envStore.add(e) envStore.add(e) s.Assert().Len(envStore.store, 1) } func (s *EnvStoreTestSuite) TestRecord() { testCases := []struct { name string env Env expectedEnvStore *envStore }{ { name: "record exists env", env: Env{ Name: "name", Value: "value", Exists: true, }, expectedEnvStore: &envStore{store: map[string]Env{ "name": { Name: "name", Value: "value", Exists: true, }, }}, }, { name: "record exists env, but its value is empty", env: Env{ Name: "name", Value: "", Exists: true, }, expectedEnvStore: &envStore{store: map[string]Env{ "name": { Name: "name", Value: "", Exists: true, }, }}, }, { name: "record not exists env", env: Env{ Name: "name", Exists: false, }, expectedEnvStore: &envStore{store: map[string]Env{ "name": { Name: "name", Exists: false, }, }}, }, } for _, tc := range testCases { s.Run(tc.name, func() { if tc.env.Exists { s.Assert().NoError(os.Setenv(tc.env.Name, tc.env.Value)) } envStore := newEnvStore() envStore.Record(tc.env.Name) s.Assert().Equal(tc.expectedEnvStore, envStore) if tc.env.Exists { s.Assert().NoError(os.Unsetenv(tc.env.Name)) } }) } } func (s *EnvStoreTestSuite) TestRestore() { testCases := []struct { name string env Env expectedEnvValue string expectedEnvExists bool }{ { name: "exists env", env: Env{ Name: "name", Value: "value", Exists: true, }, expectedEnvValue: "value", expectedEnvExists: true, }, { name: "no exists env", env: Env{ Name: "name", Exists: false, }, expectedEnvExists: false, }, } for _, tc := range testCases { s.Run(tc.name, func() { envStore := newEnvStore() envStore.add(tc.env) // Backup backup := newEnvStore() backup.Record(tc.env.Name) s.Require().NoError(os.Unsetenv(tc.env.Name)) s.Assert().NoError(envStore.Restore()) v, exists := os.LookupEnv(tc.env.Name) s.Assert().Equal(tc.expectedEnvValue, v) s.Assert().Equal(tc.expectedEnvExists, exists) // Restore s.Require().NoError(backup.Restore()) }) } } func (s *EnvStoreTestSuite) Test_setEnv() { testCases := []struct { name string key string value string expectedEnvStore *envStore expectedEnvValue string expectedEnvExists bool }{ { name: "normal", key: "name", value: "value", expectedEnvStore: &envStore{store: map[string]Env{ "name": { Name: "name", Value: "other value", Exists: true, }, }}, expectedEnvValue: "value", expectedEnvExists: true, }, } for _, tc := range testCases { s.Run(tc.name, func() { envStore := newEnvStore() // Backup backup := newEnvStore() backup.Record(tc.key) s.Require().NoError(os.Setenv(tc.key, "other value")) s.Assert().NoError(envStore.setEnv(tc.key, tc.value)) s.Assert().Equal(tc.expectedEnvStore, envStore) v, exists := os.LookupEnv(tc.key) s.Assert().Equal(tc.expectedEnvValue, v) s.Assert().Equal(tc.expectedEnvExists, exists) // Restore s.Require().NoError(backup.Restore()) }) } } func TestEnvStoreTestSuite(t *testing.T) { suite.Run(t, new(EnvStoreTestSuite)) } func TestSetEnvVariables(t *testing.T) { envs := map[string]string{ "name1": "value1", "name2": "value2", } // Backup backup := newEnvStore() for k := range envs { backup.Record(k) } defer func() { require.NoError(t, backup.Restore()) }() store, err := SetEnvVariables(envs) assert.NoError(t, err) require.IsType(t, &envStore{}, store) concreteStore := store.(*envStore) assert.Len(t, concreteStore.store, 2) assert.Equal(t, backup, concreteStore) } golang-opentelemetry-otel-1.1.0/internal/internaltest/errors.go000066400000000000000000000014241414226744000250130ustar00rootroot00000000000000// 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 internaltest type TestError string var _ error = TestError("") func NewTestError(s string) error { return TestError(s) } func (e TestError) Error() string { return string(e) } golang-opentelemetry-otel-1.1.0/internal/internaltest/harness.go000066400000000000000000000215141414226744000251440ustar00rootroot00000000000000// 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 internaltest // import "go.opentelemetry.io/otel/internal/internaltest" import ( "context" "fmt" "sync" "testing" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/internal/matchers" "go.opentelemetry.io/otel/trace" ) // Harness is a testing harness used to test implementations of the // OpenTelemetry API. type Harness struct { t *testing.T } // NewHarness returns an instantiated *Harness using t. func NewHarness(t *testing.T) *Harness { return &Harness{ t: t, } } // TestTracerProvider runs validation tests for an implementation of the OpenTelemetry // TracerProvider API. func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) { h.t.Run("#Start", func(t *testing.T) { t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) tp1 := subjectFactory() tp2 := subjectFactory() e.Expect(tp1).NotToEqual(tp2) }) t.Run("all methods are safe to be called concurrently", func(t *testing.T) { t.Parallel() runner := func(tp trace.TracerProvider) <-chan struct{} { done := make(chan struct{}) go func(tp trace.TracerProvider) { var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func(name, version string) { _ = tp.Tracer(name, trace.WithInstrumentationVersion(version)) wg.Done() }(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i)) } wg.Wait() done <- struct{}{} }(tp) return done } matchers.NewExpecter(t).Expect(func() { // Run with multiple TracerProvider to ensure they encapsulate // their own Tracers. tp1 := subjectFactory() tp2 := subjectFactory() done1 := runner(tp1) done2 := runner(tp2) <-done1 <-done2 }).NotToPanic() }) }) } // TestTracer runs validation tests for an implementation of the OpenTelemetry // Tracer API. func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { h.t.Run("#Start", func(t *testing.T) { t.Run("propagates the original context", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() ctxKey := testCtxKey{} ctxValue := "ctx value" ctx := context.WithValue(context.Background(), ctxKey, ctxValue) ctx, _ = subject.Start(ctx, "test") e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue) }) t.Run("returns a span containing the expected properties", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() _, span := subject.Start(context.Background(), "test") e.Expect(span).NotToBeNil() e.Expect(span.SpanContext().IsValid()).ToBeTrue() }) t.Run("stores the span on the provided context", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() ctx, span := subject.Start(context.Background(), "test") e.Expect(span).NotToBeNil() e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{}) e.Expect(trace.SpanFromContext(ctx)).ToEqual(span) }) t.Run("starts spans with unique trace and span IDs", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() _, span1 := subject.Start(context.Background(), "span1") _, span2 := subject.Start(context.Background(), "span2") sc1 := span1.SpanContext() sc2 := span2.SpanContext() e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID()) e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID()) }) t.Run("propagates a parent's trace ID through the context", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() ctx, parent := subject.Start(context.Background(), "parent") _, child := subject.Start(ctx, "child") psc := parent.SpanContext() csc := child.SpanContext() e.Expect(csc.TraceID()).ToEqual(psc.TraceID()) e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() ctx, parent := subject.Start(context.Background(), "parent") _, child := subject.Start(ctx, "child", trace.WithNewRoot()) psc := parent.SpanContext() csc := child.SpanContext() e.Expect(csc.TraceID()).NotToEqual(psc.TraceID()) e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() _, remoteParent := subject.Start(context.Background(), "remote parent") parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext()) _, child := subject.Start(parentCtx, "child") psc := remoteParent.SpanContext() csc := child.SpanContext() e.Expect(csc.TraceID()).ToEqual(psc.TraceID()) e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) subject := subjectFactory() _, remoteParent := subject.Start(context.Background(), "remote parent") parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext()) _, child := subject.Start(parentCtx, "child", trace.WithNewRoot()) psc := remoteParent.SpanContext() csc := child.SpanContext() e.Expect(csc.TraceID()).NotToEqual(psc.TraceID()) e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("all methods are safe to be called concurrently", func(t *testing.T) { t.Parallel() e := matchers.NewExpecter(t) tracer := subjectFactory() ctx, parent := tracer.Start(context.Background(), "span") runner := func(tp trace.Tracer) <-chan struct{} { done := make(chan struct{}) go func(tp trace.Tracer) { var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func(name string) { defer wg.Done() _, child := tp.Start(ctx, name) psc := parent.SpanContext() csc := child.SpanContext() e.Expect(csc.TraceID()).ToEqual(psc.TraceID()) e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }(fmt.Sprintf("span %d", i)) } wg.Wait() done <- struct{}{} }(tp) return done } e.Expect(func() { done := runner(tracer) <-done }).NotToPanic() }) }) h.testSpan(subjectFactory) } func (h *Harness) testSpan(tracerFactory func() trace.Tracer) { var methods = map[string]func(span trace.Span){ "#End": func(span trace.Span) { span.End() }, "#AddEvent": func(span trace.Span) { span.AddEvent("test event") }, "#AddEventWithTimestamp": func(span trace.Span) { span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second))) }, "#SetStatus": func(span trace.Span) { span.SetStatus(codes.Error, "internal") }, "#SetName": func(span trace.Span) { span.SetName("new name") }, "#SetAttributes": func(span trace.Span) { span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123)) }, } var mechanisms = map[string]func() trace.Span{ "Span created via Tracer#Start": func() trace.Span { tracer := tracerFactory() _, subject := tracer.Start(context.Background(), "test") return subject }, "Span created via span.TracerProvider()": func() trace.Span { ctx, spanA := tracerFactory().Start(context.Background(), "span1") _, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2") return spanB }, } for mechanismName, mechanism := range mechanisms { h.t.Run(mechanismName, func(t *testing.T) { for methodName, method := range methods { t.Run(methodName, func(t *testing.T) { t.Run("is thread-safe", func(t *testing.T) { t.Parallel() span := mechanism() wg := &sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() method(span) }() go func() { defer wg.Done() method(span) }() wg.Wait() }) }) } t.Run("#End", func(t *testing.T) { t.Run("can be called multiple times", func(t *testing.T) { t.Parallel() span := mechanism() span.End() span.End() }) }) }) } } type testCtxKey struct{} golang-opentelemetry-otel-1.1.0/internal/internaltest/text_map_carrier.go000066400000000000000000000070321414226744000270300ustar00rootroot00000000000000// 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 internaltest import ( "sync" "testing" "go.opentelemetry.io/otel/propagation" ) // TextMapCarrier is a storage medium for a TextMapPropagator used in testing. // The methods of a TextMapCarrier are concurrent safe. type TextMapCarrier struct { mtx sync.Mutex gets []string sets [][2]string data map[string]string } var _ propagation.TextMapCarrier = (*TextMapCarrier)(nil) // NewTextMapCarrier returns a new *TextMapCarrier populated with data. func NewTextMapCarrier(data map[string]string) *TextMapCarrier { copied := make(map[string]string, len(data)) for k, v := range data { copied[k] = v } return &TextMapCarrier{data: copied} } // Keys returns the keys for which this carrier has a value. func (c *TextMapCarrier) Keys() []string { c.mtx.Lock() defer c.mtx.Unlock() result := make([]string, 0, len(c.data)) for k := range c.data { result = append(result, k) } return result } // Get returns the value associated with the passed key. func (c *TextMapCarrier) Get(key string) string { c.mtx.Lock() defer c.mtx.Unlock() c.gets = append(c.gets, key) return c.data[key] } // GotKey tests if c.Get has been called for key. func (c *TextMapCarrier) GotKey(t *testing.T, key string) bool { c.mtx.Lock() defer c.mtx.Unlock() for _, k := range c.gets { if k == key { return true } } t.Errorf("TextMapCarrier.Get(%q) has not been called", key) return false } // GotN tests if n calls to c.Get have been made. func (c *TextMapCarrier) GotN(t *testing.T, n int) bool { c.mtx.Lock() defer c.mtx.Unlock() if len(c.gets) != n { t.Errorf("TextMapCarrier.Get was called %d times, not %d", len(c.gets), n) return false } return true } // Set stores the key-value pair. func (c *TextMapCarrier) Set(key, value string) { c.mtx.Lock() defer c.mtx.Unlock() c.sets = append(c.sets, [2]string{key, value}) c.data[key] = value } // SetKeyValue tests if c.Set has been called for the key-value pair. func (c *TextMapCarrier) SetKeyValue(t *testing.T, key, value string) bool { c.mtx.Lock() defer c.mtx.Unlock() var vals []string for _, pair := range c.sets { if key == pair[0] { if value == pair[1] { return true } vals = append(vals, pair[1]) } } if len(vals) > 0 { t.Errorf("TextMapCarrier.Set called with %q and %v values, but not %s", key, vals, value) } t.Errorf("TextMapCarrier.Set(%q,%q) has not been called", key, value) return false } // SetN tests if n calls to c.Set have been made. func (c *TextMapCarrier) SetN(t *testing.T, n int) bool { c.mtx.Lock() defer c.mtx.Unlock() if len(c.sets) != n { t.Errorf("TextMapCarrier.Set was called %d times, not %d", len(c.sets), n) return false } return true } // Reset zeros out the recording state and sets the carried values to data. func (c *TextMapCarrier) Reset(data map[string]string) { copied := make(map[string]string, len(data)) for k, v := range data { copied[k] = v } c.mtx.Lock() defer c.mtx.Unlock() c.gets = nil c.sets = nil c.data = copied } golang-opentelemetry-otel-1.1.0/internal/internaltest/text_map_carrier_test.go000066400000000000000000000044311414226744000300670ustar00rootroot00000000000000// 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 internaltest import ( "reflect" "testing" ) var ( key, value = "test", "true" ) func TestTextMapCarrierKeys(t *testing.T) { tmc := NewTextMapCarrier(map[string]string{key: value}) expected, actual := []string{key}, tmc.Keys() if !reflect.DeepEqual(actual, expected) { t.Errorf("expected tmc.Keys() to be %v but it was %v", expected, actual) } } func TestTextMapCarrierGet(t *testing.T) { tmc := NewTextMapCarrier(map[string]string{key: value}) tmc.GotN(t, 0) if got := tmc.Get("empty"); got != "" { t.Errorf("TextMapCarrier.Get returned %q for an empty key", got) } tmc.GotKey(t, "empty") tmc.GotN(t, 1) if got := tmc.Get(key); got != value { t.Errorf("TextMapCarrier.Get(%q) returned %q, want %q", key, got, value) } tmc.GotKey(t, key) tmc.GotN(t, 2) } func TestTextMapCarrierSet(t *testing.T) { tmc := NewTextMapCarrier(nil) tmc.SetN(t, 0) tmc.Set(key, value) if got, ok := tmc.data[key]; !ok { t.Errorf("TextMapCarrier.Set(%q,%q) failed to store pair", key, value) } else if got != value { t.Errorf("TextMapCarrier.Set(%q,%q) stored (%q,%q), not (%q,%q)", key, value, key, got, key, value) } tmc.SetKeyValue(t, key, value) tmc.SetN(t, 1) } func TestTextMapCarrierReset(t *testing.T) { tmc := NewTextMapCarrier(map[string]string{key: value}) tmc.GotN(t, 0) tmc.SetN(t, 0) tmc.Reset(nil) tmc.GotN(t, 0) tmc.SetN(t, 0) if got := tmc.Get(key); got != "" { t.Error("TextMapCarrier.Reset() failed to clear initial data") } tmc.GotN(t, 1) tmc.GotKey(t, key) tmc.Set(key, value) tmc.SetKeyValue(t, key, value) tmc.SetN(t, 1) tmc.Reset(nil) tmc.GotN(t, 0) tmc.SetN(t, 0) if got := tmc.Get(key); got != "" { t.Error("TextMapCarrier.Reset() failed to clear data") } } golang-opentelemetry-otel-1.1.0/internal/internaltest/text_map_propagator.go000066400000000000000000000065071414226744000275650ustar00rootroot00000000000000// 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 internaltest import ( "context" "fmt" "strconv" "strings" "testing" "go.opentelemetry.io/otel/propagation" ) type ctxKeyType string type state struct { Injections uint64 Extractions uint64 } func newState(encoded string) state { if encoded == "" { return state{} } split := strings.SplitN(encoded, ",", 2) injects, _ := strconv.ParseUint(split[0], 10, 64) extracts, _ := strconv.ParseUint(split[1], 10, 64) return state{ Injections: injects, Extractions: extracts, } } func (s state) String() string { return fmt.Sprintf("%d,%d", s.Injections, s.Extractions) } // TextMapPropagator is a propagation.TextMapPropagator used for testing. type TextMapPropagator struct { name string ctxKey ctxKeyType } var _ propagation.TextMapPropagator = (*TextMapPropagator)(nil) // NewTextMapPropagator returns a new TextMapPropagator for testing. It will // use name as the key it injects into a TextMapCarrier when Inject is called. func NewTextMapPropagator(name string) *TextMapPropagator { return &TextMapPropagator{name: name, ctxKey: ctxKeyType(name)} } func (p *TextMapPropagator) stateFromContext(ctx context.Context) state { if v := ctx.Value(p.ctxKey); v != nil { if s, ok := v.(state); ok { return s } } return state{} } func (p *TextMapPropagator) stateFromCarrier(carrier propagation.TextMapCarrier) state { return newState(carrier.Get(p.name)) } // Inject sets cross-cutting concerns for p from ctx into carrier. func (p *TextMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { s := p.stateFromContext(ctx) s.Injections++ carrier.Set(p.name, s.String()) } // InjectedN tests if p has made n injections to carrier. func (p *TextMapPropagator) InjectedN(t *testing.T, carrier *TextMapCarrier, n int) bool { if actual := p.stateFromCarrier(carrier).Injections; actual != uint64(n) { t.Errorf("TextMapPropagator{%q} injected %d times, not %d", p.name, actual, n) return false } return true } // Extract reads cross-cutting concerns for p from carrier into ctx. func (p *TextMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { s := p.stateFromCarrier(carrier) s.Extractions++ return context.WithValue(ctx, p.ctxKey, s) } // ExtractedN tests if p has made n extractions from the lineage of ctx. // nolint (context is not first arg) func (p *TextMapPropagator) ExtractedN(t *testing.T, ctx context.Context, n int) bool { if actual := p.stateFromContext(ctx).Extractions; actual != uint64(n) { t.Errorf("TextMapPropagator{%q} extracted %d time, not %d", p.name, actual, n) return false } return true } // Fields returns the name of p as the key who's value is set with Inject. func (p *TextMapPropagator) Fields() []string { return []string{p.name} } golang-opentelemetry-otel-1.1.0/internal/internaltest/text_map_propagator_test.go000066400000000000000000000042301414226744000306130ustar00rootroot00000000000000// 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 internaltest import ( "context" "testing" ) func TestTextMapPropagatorInjectExtract(t *testing.T) { name := "testing" ctx := context.Background() carrier := NewTextMapCarrier(map[string]string{name: value}) propagator := NewTextMapPropagator(name) propagator.Inject(ctx, carrier) // Carrier value overridden with state. if carrier.SetKeyValue(t, name, "1,0") { // Ensure nothing has been extracted yet. propagator.ExtractedN(t, ctx, 0) // Test the injection was counted. propagator.InjectedN(t, carrier, 1) } ctx = propagator.Extract(ctx, carrier) v := ctx.Value(ctxKeyType(name)) if v == nil { t.Error("TextMapPropagator.Extract failed to extract state") } if s, ok := v.(state); !ok { t.Error("TextMapPropagator.Extract did not extract proper state") } else if s.Extractions != 1 { t.Error("TextMapPropagator.Extract did not increment state.Extractions") } if carrier.GotKey(t, name) { // Test the extraction was counted. propagator.ExtractedN(t, ctx, 1) // Ensure no additional injection was recorded. propagator.InjectedN(t, carrier, 1) } } func TestTextMapPropagatorFields(t *testing.T) { name := "testing" propagator := NewTextMapPropagator(name) if got := propagator.Fields(); len(got) != 1 { t.Errorf("TextMapPropagator.Fields returned %d fields, want 1", len(got)) } else if got[0] != name { t.Errorf("TextMapPropagator.Fields returned %q, want %q", got[0], name) } } func TestNewStateEmpty(t *testing.T) { if want, got := (state{}), newState(""); got != want { t.Errorf("newState(\"\") returned %v, want %v", got, want) } } golang-opentelemetry-otel-1.1.0/internal/matchers/000077500000000000000000000000001414226744000222415ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/matchers/expectation.go000066400000000000000000000174171414226744000251250ustar00rootroot00000000000000// 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 matchers import ( "fmt" "reflect" "regexp" "runtime/debug" "strings" "testing" "time" ) var ( stackTracePruneRE = regexp.MustCompile(`runtime\/debug|testing|internal\/matchers`) ) type Expectation struct { t *testing.T actual interface{} } func (e *Expectation) ToEqual(expected interface{}) { e.verifyExpectedNotNil(expected) if !reflect.DeepEqual(e.actual, expected) { e.fail(fmt.Sprintf("Expected\n\t%v\nto equal\n\t%v", e.actual, expected)) } } func (e *Expectation) NotToEqual(expected interface{}) { e.verifyExpectedNotNil(expected) if reflect.DeepEqual(e.actual, expected) { e.fail(fmt.Sprintf("Expected\n\t%v\nnot to equal\n\t%v", e.actual, expected)) } } func (e *Expectation) ToBeNil() { if e.actual != nil { e.fail(fmt.Sprintf("Expected\n\t%v\nto be nil", e.actual)) } } func (e *Expectation) NotToBeNil() { if e.actual == nil { e.fail(fmt.Sprintf("Expected\n\t%v\nnot to be nil", e.actual)) } } func (e *Expectation) ToBeTrue() { switch a := e.actual.(type) { case bool: if e.actual == false { e.fail(fmt.Sprintf("Expected\n\t%v\nto be true", e.actual)) } default: e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) } } func (e *Expectation) ToBeFalse() { switch a := e.actual.(type) { case bool: if e.actual == true { e.fail(fmt.Sprintf("Expected\n\t%v\nto be false", e.actual)) } default: e.fail(fmt.Sprintf("Cannot check if non-bool value\n\t%v\nis truthy", a)) } } func (e *Expectation) NotToPanic() { switch a := e.actual.(type) { case func(): func() { defer func() { if recovered := recover(); recovered != nil { e.fail(fmt.Sprintf("Expected panic\n\t%v\nto have not been raised", recovered)) } }() a() }() default: e.fail(fmt.Sprintf("Cannot check if non-func value\n\t%v\nis truthy", a)) } } func (e *Expectation) ToSucceed() { switch actual := e.actual.(type) { case error: if actual != nil { e.fail(fmt.Sprintf("Expected error\n\t%v\nto have succeeded", actual)) } default: e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nsucceeded", actual)) } } func (e *Expectation) ToMatchError(expected interface{}) { e.verifyExpectedNotNil(expected) actual, ok := e.actual.(error) if !ok { e.fail(fmt.Sprintf("Cannot check if non-error value\n\t%v\nmatches error", e.actual)) } switch expected := expected.(type) { case error: if !reflect.DeepEqual(actual, expected) { e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) } case string: if actual.Error() != expected { e.fail(fmt.Sprintf("Expected\n\t%v\nto match error\n\t%v", actual, expected)) } default: e.fail(fmt.Sprintf("Cannot match\n\t%v\nagainst non-error\n\t%v", actual, expected)) } } func (e *Expectation) ToContain(expected interface{}) { actualValue := reflect.ValueOf(e.actual) actualKind := actualValue.Kind() switch actualKind { case reflect.Array, reflect.Slice: default: e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) return } expectedValue := reflect.ValueOf(expected) expectedKind := expectedValue.Kind() switch expectedKind { case reflect.Array, reflect.Slice: default: expectedValue = reflect.ValueOf([]interface{}{expected}) } for i := 0; i < expectedValue.Len(); i++ { var contained bool expectedElem := expectedValue.Index(i).Interface() for j := 0; j < actualValue.Len(); j++ { if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { contained = true break } } if !contained { e.fail(fmt.Sprintf("Expected\n\t%v\nto contain\n\t%v", e.actual, expectedElem)) return } } } func (e *Expectation) NotToContain(expected interface{}) { actualValue := reflect.ValueOf(e.actual) actualKind := actualValue.Kind() switch actualKind { case reflect.Array, reflect.Slice: default: e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", e.actual)) return } expectedValue := reflect.ValueOf(expected) expectedKind := expectedValue.Kind() switch expectedKind { case reflect.Array, reflect.Slice: default: expectedValue = reflect.ValueOf([]interface{}{expected}) } for i := 0; i < expectedValue.Len(); i++ { expectedElem := expectedValue.Index(i).Interface() for j := 0; j < actualValue.Len(); j++ { if reflect.DeepEqual(actualValue.Index(j).Interface(), expectedElem) { e.fail(fmt.Sprintf("Expected\n\t%v\nnot to contain\n\t%v", e.actual, expectedElem)) return } } } } func (e *Expectation) ToMatchInAnyOrder(expected interface{}) { expectedValue := reflect.ValueOf(expected) expectedKind := expectedValue.Kind() switch expectedKind { case reflect.Array, reflect.Slice: default: e.fail(fmt.Sprintf("Expected\n\t%v\nto be an array", expected)) return } actualValue := reflect.ValueOf(e.actual) actualKind := actualValue.Kind() if actualKind != expectedKind { e.fail(fmt.Sprintf("Expected\n\t%v\nto be the same type as\n\t%v", e.actual, expected)) return } if actualValue.Len() != expectedValue.Len() { e.fail(fmt.Sprintf("Expected\n\t%v\nto have the same length as\n\t%v", e.actual, expected)) return } var unmatched []interface{} for i := 0; i < expectedValue.Len(); i++ { unmatched = append(unmatched, expectedValue.Index(i).Interface()) } for i := 0; i < actualValue.Len(); i++ { var found bool for j, elem := range unmatched { if reflect.DeepEqual(actualValue.Index(i).Interface(), elem) { found = true unmatched = append(unmatched[:j], unmatched[j+1:]...) break } } if !found { e.fail(fmt.Sprintf("Expected\n\t%v\nto contain the same elements as\n\t%v", e.actual, expected)) } } } func (e *Expectation) ToBeTemporally(matcher TemporalMatcher, compareTo interface{}) { if actual, ok := e.actual.(time.Time); ok { if ct, ok := compareTo.(time.Time); ok { switch matcher { case Before: if !actual.Before(ct) { e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before\n\t%v", e.actual, compareTo)) } case BeforeOrSameTime: if actual.After(ct) { e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally before or at the same time as\n\t%v", e.actual, compareTo)) } case After: if !actual.After(ct) { e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after\n\t%v", e.actual, compareTo)) } case AfterOrSameTime: if actual.Before(ct) { e.fail(fmt.Sprintf("Expected\n\t%v\nto be temporally after or at the same time as\n\t%v", e.actual, compareTo)) } default: e.fail("Cannot compare times with unexpected temporal matcher") } } else { e.fail(fmt.Sprintf("Cannot compare to non-temporal value\n\t%v", compareTo)) return } return } e.fail(fmt.Sprintf("Cannot compare non-temporal value\n\t%v", e.actual)) } func (e *Expectation) verifyExpectedNotNil(expected interface{}) { if expected == nil { e.fail("Refusing to compare with . Use `ToBeNil` or `NotToBeNil` instead.") } } func (e *Expectation) fail(msg string) { // Prune the stack trace so that it's easier to see relevant lines stack := strings.Split(string(debug.Stack()), "\n") var prunedStack []string for _, line := range stack { if !stackTracePruneRE.MatchString(line) { prunedStack = append(prunedStack, line) } } e.t.Fatalf("\n%s\n%s\n", strings.Join(prunedStack, "\n"), msg) } golang-opentelemetry-otel-1.1.0/internal/matchers/expecter.go000066400000000000000000000015541414226744000244140ustar00rootroot00000000000000// 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 matchers import ( "testing" ) type Expecter struct { t *testing.T } func NewExpecter(t *testing.T) *Expecter { return &Expecter{ t: t, } } func (a *Expecter) Expect(actual interface{}) *Expectation { return &Expectation{ t: a.t, actual: actual, } } golang-opentelemetry-otel-1.1.0/internal/matchers/package.go000066400000000000000000000012361414226744000241650ustar00rootroot00000000000000// 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 matchers // import "go.opentelemetry.io/otel/internal/matchers" golang-opentelemetry-otel-1.1.0/internal/matchers/temporal_matcher.go000066400000000000000000000014631414226744000261220ustar00rootroot00000000000000// 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 matchers type TemporalMatcher byte //nolint:revive // ignoring missing comments for unexported constants in an internal package const ( Before TemporalMatcher = iota BeforeOrSameTime After AfterOrSameTime ) golang-opentelemetry-otel-1.1.0/internal/metric/000077500000000000000000000000001414226744000217165ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/metric/async.go000066400000000000000000000114731414226744000233700ustar00rootroot00000000000000// 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 metric import ( "context" "errors" "fmt" "sync" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/sdkapi" ) //nolint:revive // ignoring missing comments for exported error in an internal package var ErrInvalidAsyncRunner = errors.New("unknown async runner type") // AsyncCollector is an interface used between the MeterImpl and the // AsyncInstrumentState helper below. This interface is implemented by // the SDK to provide support for running observer callbacks. type AsyncCollector interface { // CollectAsync passes a batch of observations to the MeterImpl. CollectAsync(labels []attribute.KeyValue, observation ...sdkapi.Observation) } // AsyncInstrumentState manages an ordered set of asynchronous // instruments and the distinct runners, taking into account batch // observer callbacks. type AsyncInstrumentState struct { lock sync.Mutex // errorOnce will use the otel.Handler to report an error // once in case of an invalid runner attempting to run. errorOnce sync.Once // runnerMap keeps the set of runners that will run each // collection interval. Singletons are entered with a real // instrument each, batch observers are entered with a nil // instrument, ensuring that when a singleton callback is used // repeatedly, it is executed repeatedly in the interval, while // when a batch callback is used repeatedly, it only executes // once per interval. runnerMap map[asyncRunnerPair]struct{} // runners maintains the set of runners in the order they were // registered. runners []asyncRunnerPair // instruments maintains the set of instruments in the order // they were registered. instruments []sdkapi.AsyncImpl } // asyncRunnerPair is a map entry for Observer callback runners. type asyncRunnerPair struct { // runner is used as a map key here. The API ensures // that all callbacks are pointers for this reason. runner sdkapi.AsyncRunner // inst refers to a non-nil instrument when `runner` is a // AsyncSingleRunner. inst sdkapi.AsyncImpl } // NewAsyncInstrumentState returns a new *AsyncInstrumentState, for // use by MeterImpl to manage running the set of observer callbacks in // the correct order. func NewAsyncInstrumentState() *AsyncInstrumentState { return &AsyncInstrumentState{ runnerMap: map[asyncRunnerPair]struct{}{}, } } // Instruments returns the asynchronous instruments managed by this // object, the set that should be checkpointed after observers are // run. func (a *AsyncInstrumentState) Instruments() []sdkapi.AsyncImpl { a.lock.Lock() defer a.lock.Unlock() return a.instruments } // Register adds a new asynchronous instrument to by managed by this // object. This should be called during NewAsyncInstrument() and // assumes that errors (e.g., duplicate registration) have already // been checked. func (a *AsyncInstrumentState) Register(inst sdkapi.AsyncImpl, runner sdkapi.AsyncRunner) { a.lock.Lock() defer a.lock.Unlock() a.instruments = append(a.instruments, inst) // asyncRunnerPair reflects this callback in the asyncRunners // list. If this is a batch runner, the instrument is nil. // If this is a single-Observer runner, the instrument is // included. This ensures that batch callbacks are called // once and single callbacks are called once per instrument. rp := asyncRunnerPair{ runner: runner, } if _, ok := runner.(sdkapi.AsyncSingleRunner); ok { rp.inst = inst } if _, ok := a.runnerMap[rp]; !ok { a.runnerMap[rp] = struct{}{} a.runners = append(a.runners, rp) } } // Run executes the complete set of observer callbacks. func (a *AsyncInstrumentState) Run(ctx context.Context, collector AsyncCollector) { a.lock.Lock() runners := a.runners a.lock.Unlock() for _, rp := range runners { // The runner must be a single or batch runner, no // other implementations are possible because the // interface has un-exported methods. if singleRunner, ok := rp.runner.(sdkapi.AsyncSingleRunner); ok { singleRunner.Run(ctx, rp.inst, collector.CollectAsync) continue } if multiRunner, ok := rp.runner.(sdkapi.AsyncBatchRunner); ok { multiRunner.Run(ctx, collector.CollectAsync) continue } a.errorOnce.Do(func() { otel.Handle(fmt.Errorf("%w: type %T (reported once)", ErrInvalidAsyncRunner, rp)) }) } } golang-opentelemetry-otel-1.1.0/internal/metric/global/000077500000000000000000000000001414226744000231565ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/metric/global/benchmark_test.go000066400000000000000000000024601414226744000265000ustar00rootroot00000000000000// 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_test import ( "context" "testing" "go.opentelemetry.io/otel/attribute" internalglobal "go.opentelemetry.io/otel/internal/metric/global" metricglobal "go.opentelemetry.io/otel/metric/global" ) func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) { // Compare with BenchmarkGlobalInt64CounterAddWithSDK() in // ../../sdk/metric/benchmark_test.go to see the overhead of the // global no-op system against a registered SDK. internalglobal.ResetForTest() ctx := context.Background() sdk := metricglobal.Meter("test") labs := []attribute.KeyValue{attribute.String("A", "B")} cnt := Must(sdk).NewInt64Counter("int64.counter") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Add(ctx, 1, labs...) } } golang-opentelemetry-otel-1.1.0/internal/metric/global/internal_test.go000066400000000000000000000021751414226744000263650ustar00rootroot00000000000000// 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_test import ( "os" "testing" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/internal/metric/global" ) // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { fieldsMap := global.AtomicFieldOffsets() fields := make([]ottest.FieldOffset, 0, len(fieldsMap)) for name, offset := range fieldsMap { fields = append(fields, ottest.FieldOffset{ Name: name, Offset: offset, }) } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } golang-opentelemetry-otel-1.1.0/internal/metric/global/meter.go000066400000000000000000000231251414226744000246240ustar00rootroot00000000000000// 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 ( "context" "sync" "sync/atomic" "unsafe" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/metric/registry" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) // This file contains the forwarding implementation of MeterProvider used as // the default global instance. Metric events using instruments provided by // this implementation are no-ops until the first Meter implementation is set // as the global provider. // // The implementation here uses Mutexes to maintain a list of active Meters in // the MeterProvider and Instruments in each Meter, under the assumption that // these interfaces are not performance-critical. // // We have the invariant that setDelegate() will be called before a new // MeterProvider implementation is registered as the global provider. Mutexes // in the MeterProvider and Meters ensure that each instrument has a delegate // before the global provider is set. // // Bound instrument operations are implemented by delegating to the // instrument after it is registered, with a sync.Once initializer to // protect against races with Release(). // // Metric uniqueness checking is implemented by calling the exported // methods of the api/metric/registry package. type meterKey struct { InstrumentationName string InstrumentationVersion string SchemaURL string } type meterProvider struct { delegate metric.MeterProvider // lock protects `delegate` and `meters`. lock sync.Mutex // meters maintains a unique entry for every named Meter // that has been registered through the global instance. meters map[meterKey]*meterEntry } type meterImpl struct { delegate unsafe.Pointer // (*metric.MeterImpl) lock sync.Mutex syncInsts []*syncImpl asyncInsts []*asyncImpl } type meterEntry struct { unique sdkapi.MeterImpl impl meterImpl } type instrument struct { descriptor sdkapi.Descriptor } type syncImpl struct { delegate unsafe.Pointer // (*sdkapi.SyncImpl) instrument } type asyncImpl struct { delegate unsafe.Pointer // (*sdkapi.AsyncImpl) instrument runner sdkapi.AsyncRunner } // SyncImpler is implemented by all of the sync metric // instruments. type SyncImpler interface { SyncImpl() sdkapi.SyncImpl } // AsyncImpler is implemented by all of the async // metric instruments. type AsyncImpler interface { AsyncImpl() sdkapi.AsyncImpl } type syncHandle struct { delegate unsafe.Pointer // (*sdkapi.BoundInstrumentImpl) inst *syncImpl labels []attribute.KeyValue initialize sync.Once } var _ metric.MeterProvider = &meterProvider{} var _ sdkapi.MeterImpl = &meterImpl{} var _ sdkapi.InstrumentImpl = &syncImpl{} var _ sdkapi.BoundSyncImpl = &syncHandle{} var _ sdkapi.AsyncImpl = &asyncImpl{} func (inst *instrument) Descriptor() sdkapi.Descriptor { return inst.descriptor } // MeterProvider interface and delegation func newMeterProvider() *meterProvider { return &meterProvider{ meters: map[meterKey]*meterEntry{}, } } func (p *meterProvider) setDelegate(provider metric.MeterProvider) { p.lock.Lock() defer p.lock.Unlock() p.delegate = provider for key, entry := range p.meters { entry.impl.setDelegate(key, provider) } p.meters = nil } func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { p.lock.Lock() defer p.lock.Unlock() if p.delegate != nil { return p.delegate.Meter(instrumentationName, opts...) } cfg := metric.NewMeterConfig(opts...) key := meterKey{ InstrumentationName: instrumentationName, InstrumentationVersion: cfg.InstrumentationVersion(), SchemaURL: cfg.SchemaURL(), } entry, ok := p.meters[key] if !ok { entry = &meterEntry{} // Note: This code implements its own MeterProvider // name-uniqueness logic because there is // synchronization required at the moment of // delegation. We use the same instrument-uniqueness // checking the real SDK uses here: entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl) p.meters[key] = entry } return metric.WrapMeterImpl(entry.unique) } // Meter interface and delegation func (m *meterImpl) setDelegate(key meterKey, provider metric.MeterProvider) { m.lock.Lock() defer m.lock.Unlock() d := new(sdkapi.MeterImpl) *d = provider.Meter( key.InstrumentationName, metric.WithInstrumentationVersion(key.InstrumentationVersion), metric.WithSchemaURL(key.SchemaURL), ).MeterImpl() m.delegate = unsafe.Pointer(d) for _, inst := range m.syncInsts { inst.setDelegate(*d) } m.syncInsts = nil for _, obs := range m.asyncInsts { obs.setDelegate(*d) } m.asyncInsts = nil } func (m *meterImpl) NewSyncInstrument(desc sdkapi.Descriptor) (sdkapi.SyncImpl, error) { m.lock.Lock() defer m.lock.Unlock() if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { return (*meterPtr).NewSyncInstrument(desc) } inst := &syncImpl{ instrument: instrument{ descriptor: desc, }, } m.syncInsts = append(m.syncInsts, inst) return inst, nil } // Synchronous delegation func (inst *syncImpl) setDelegate(d sdkapi.MeterImpl) { implPtr := new(sdkapi.SyncImpl) var err error *implPtr, err = d.NewSyncInstrument(inst.descriptor) if err != nil { // TODO: There is no standard way to deliver this error to the user. // See https://github.com/open-telemetry/opentelemetry-go/issues/514 // Note that the default SDK will not generate any errors yet, this is // only for added safety. panic(err) } atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr)) } func (inst *syncImpl) Implementation() interface{} { if implPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { return (*implPtr).Implementation() } return inst } func (inst *syncImpl) Bind(labels []attribute.KeyValue) sdkapi.BoundSyncImpl { if implPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { return (*implPtr).Bind(labels) } return &syncHandle{ inst: inst, labels: labels, } } func (bound *syncHandle) Unbind() { bound.initialize.Do(func() {}) implPtr := (*sdkapi.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate)) if implPtr == nil { return } (*implPtr).Unbind() } // Async delegation func (m *meterImpl) NewAsyncInstrument( desc sdkapi.Descriptor, runner sdkapi.AsyncRunner, ) (sdkapi.AsyncImpl, error) { m.lock.Lock() defer m.lock.Unlock() if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { return (*meterPtr).NewAsyncInstrument(desc, runner) } inst := &asyncImpl{ instrument: instrument{ descriptor: desc, }, runner: runner, } m.asyncInsts = append(m.asyncInsts, inst) return inst, nil } func (obs *asyncImpl) Implementation() interface{} { if implPtr := (*sdkapi.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil { return (*implPtr).Implementation() } return obs } func (obs *asyncImpl) setDelegate(d sdkapi.MeterImpl) { implPtr := new(sdkapi.AsyncImpl) var err error *implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner) if err != nil { // TODO: There is no standard way to deliver this error to the user. // See https://github.com/open-telemetry/opentelemetry-go/issues/514 // Note that the default SDK will not generate any errors yet, this is // only for added safety. panic(err) } atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr)) } // Metric updates func (m *meterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) { if delegatePtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil { (*delegatePtr).RecordBatch(ctx, labels, measurements...) } } func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) { if instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil { (*instPtr).RecordOne(ctx, number, labels) } } // Bound instrument initialization func (bound *syncHandle) RecordOne(ctx context.Context, number number.Number) { instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&bound.inst.delegate)) if instPtr == nil { return } var implPtr *sdkapi.BoundSyncImpl bound.initialize.Do(func() { implPtr = new(sdkapi.BoundSyncImpl) *implPtr = (*instPtr).Bind(bound.labels) atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr)) }) if implPtr == nil { implPtr = (*sdkapi.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate)) } // This may still be nil if instrument was created and bound // without a delegate, then the instrument was set to have a // delegate and unbound. if implPtr == nil { return } (*implPtr).RecordOne(ctx, number) } func AtomicFieldOffsets() map[string]uintptr { return map[string]uintptr{ "meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate), "meterImpl.delegate": unsafe.Offsetof(meterImpl{}.delegate), "syncImpl.delegate": unsafe.Offsetof(syncImpl{}.delegate), "asyncImpl.delegate": unsafe.Offsetof(asyncImpl{}.delegate), "syncHandle.delegate": unsafe.Offsetof(syncHandle{}.delegate), } } golang-opentelemetry-otel-1.1.0/internal/metric/global/meter_test.go000066400000000000000000000214251414226744000256640ustar00rootroot00000000000000// 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_test import ( "context" "errors" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/metric/global" "go.opentelemetry.io/otel/metric" metricglobal "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) var Must = metric.Must var asInt = number.NewInt64Number var asFloat = number.NewFloat64Number func TestDirect(t *testing.T) { global.ResetForTest() ctx := context.Background() meter1 := metricglobal.Meter("test1", metric.WithInstrumentationVersion("semver:v1.0.0")) meter2 := metricglobal.Meter("test2", metric.WithSchemaURL("hello")) library1 := metrictest.Library{ InstrumentationName: "test1", InstrumentationVersion: "semver:v1.0.0", } library2 := metrictest.Library{ InstrumentationName: "test2", SchemaURL: "hello", } labels1 := []attribute.KeyValue{attribute.String("A", "B")} labels2 := []attribute.KeyValue{attribute.String("C", "D")} labels3 := []attribute.KeyValue{attribute.String("E", "F")} counter := Must(meter1).NewInt64Counter("test.counter") counter.Add(ctx, 1, labels1...) counter.Add(ctx, 1, labels1...) histogram := Must(meter1).NewFloat64Histogram("test.histogram") histogram.Record(ctx, 1, labels1...) histogram.Record(ctx, 2, labels1...) _ = Must(meter1).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(1., labels1...) result.Observe(2., labels2...) }) _ = Must(meter1).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(1, labels1...) result.Observe(2, labels2...) }) second := Must(meter2).NewFloat64Histogram("test.second") second.Record(ctx, 1, labels3...) second.Record(ctx, 2, labels3...) provider := metrictest.NewMeterProvider() metricglobal.SetMeterProvider(provider) counter.Add(ctx, 1, labels1...) histogram.Record(ctx, 3, labels1...) second.Record(ctx, 3, labels3...) provider.RunAsyncInstruments() measurements := metrictest.AsStructs(provider.MeasurementBatches) require.EqualValues(t, []metrictest.Measured{ { Name: "test.counter", Library: library1, Labels: metrictest.LabelsToMap(labels1...), Number: asInt(1), }, { Name: "test.histogram", Library: library1, Labels: metrictest.LabelsToMap(labels1...), Number: asFloat(3), }, { Name: "test.second", Library: library2, Labels: metrictest.LabelsToMap(labels3...), Number: asFloat(3), }, { Name: "test.gauge.float", Library: library1, Labels: metrictest.LabelsToMap(labels1...), Number: asFloat(1), }, { Name: "test.gauge.float", Library: library1, Labels: metrictest.LabelsToMap(labels2...), Number: asFloat(2), }, { Name: "test.gauge.int", Library: library1, Labels: metrictest.LabelsToMap(labels1...), Number: asInt(1), }, { Name: "test.gauge.int", Library: library1, Labels: metrictest.LabelsToMap(labels2...), Number: asInt(2), }, }, measurements, ) } func TestBound(t *testing.T) { global.ResetForTest() // Note: this test uses opposite Float64/Int64 number kinds // vs. the above, to cover all the instruments. ctx := context.Background() glob := metricglobal.Meter( "test", metric.WithInstrumentationVersion("semver:test-1.0"), metric.WithSchemaURL("schema://url"), ) labels1 := []attribute.KeyValue{attribute.String("A", "B")} counter := Must(glob).NewFloat64Counter("test.counter") boundC := counter.Bind(labels1...) boundC.Add(ctx, 1) boundC.Add(ctx, 1) histogram := Must(glob).NewInt64Histogram("test.histogram") boundM := histogram.Bind(labels1...) boundM.Record(ctx, 1) boundM.Record(ctx, 2) provider := metrictest.NewMeterProvider() metricglobal.SetMeterProvider(provider) boundC.Add(ctx, 1) boundM.Record(ctx, 3) library := metrictest.Library{ InstrumentationName: "test", InstrumentationVersion: "semver:test-1.0", SchemaURL: "schema://url", } require.EqualValues(t, []metrictest.Measured{ { Name: "test.counter", Library: library, Labels: metrictest.LabelsToMap(labels1...), Number: asFloat(1), }, { Name: "test.histogram", Library: library, Labels: metrictest.LabelsToMap(labels1...), Number: asInt(3), }, }, metrictest.AsStructs(provider.MeasurementBatches)) boundC.Unbind() boundM.Unbind() } func TestUnbind(t *testing.T) { // Tests Unbind with SDK never installed. global.ResetForTest() glob := metricglobal.Meter("test") labels1 := []attribute.KeyValue{attribute.String("A", "B")} counter := Must(glob).NewFloat64Counter("test.counter") boundC := counter.Bind(labels1...) histogram := Must(glob).NewInt64Histogram("test.histogram") boundM := histogram.Bind(labels1...) boundC.Unbind() boundM.Unbind() } func TestUnbindThenRecordOne(t *testing.T) { global.ResetForTest() ctx := context.Background() provider := metrictest.NewMeterProvider() meter := metricglobal.Meter("test") counter := Must(meter).NewInt64Counter("test.counter") boundC := counter.Bind() metricglobal.SetMeterProvider(provider) boundC.Unbind() require.NotPanics(t, func() { boundC.Add(ctx, 1) }) require.Equal(t, 0, len(provider.MeasurementBatches)) } type meterProviderWithConstructorError struct { metric.MeterProvider } type meterWithConstructorError struct { sdkapi.MeterImpl } func (m *meterProviderWithConstructorError) Meter(iName string, opts ...metric.MeterOption) metric.Meter { return metric.WrapMeterImpl(&meterWithConstructorError{m.MeterProvider.Meter(iName, opts...).MeterImpl()}) } func (m *meterWithConstructorError) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) { return sdkapi.NewNoopSyncInstrument(), errors.New("constructor error") } func TestErrorInDeferredConstructor(t *testing.T) { global.ResetForTest() ctx := context.Background() meter := metricglobal.GetMeterProvider().Meter("builtin") c1 := Must(meter).NewInt64Counter("test") c2 := Must(meter).NewInt64Counter("test") provider := metrictest.NewMeterProvider() sdk := &meterProviderWithConstructorError{provider} require.Panics(t, func() { metricglobal.SetMeterProvider(sdk) }) c1.Add(ctx, 1) c2.Add(ctx, 2) } func TestImplementationIndirection(t *testing.T) { global.ResetForTest() // Test that Implementation() does the proper indirection, i.e., // returns the implementation interface not the global, after // registered. meter1 := metricglobal.Meter("test1") // Sync: no SDK yet counter := Must(meter1).NewInt64Counter("interface.counter") ival := counter.Measurement(1).SyncImpl().Implementation() require.NotNil(t, ival) _, ok := ival.(*metrictest.Sync) require.False(t, ok) // Async: no SDK yet gauge := Must(meter1).NewFloat64GaugeObserver( "interface.gauge", func(_ context.Context, result metric.Float64ObserverResult) {}, ) ival = gauge.AsyncImpl().Implementation() require.NotNil(t, ival) _, ok = ival.(*metrictest.Async) require.False(t, ok) // Register the SDK provider := metrictest.NewMeterProvider() metricglobal.SetMeterProvider(provider) // Repeat the above tests // Sync ival = counter.Measurement(1).SyncImpl().Implementation() require.NotNil(t, ival) _, ok = ival.(*metrictest.Sync) require.True(t, ok) // Async ival = gauge.AsyncImpl().Implementation() require.NotNil(t, ival) _, ok = ival.(*metrictest.Async) require.True(t, ok) } func TestRecordBatchMock(t *testing.T) { global.ResetForTest() meter := metricglobal.GetMeterProvider().Meter("builtin") counter := Must(meter).NewInt64Counter("test.counter") meter.RecordBatch(context.Background(), nil, counter.Measurement(1)) provider := metrictest.NewMeterProvider() metricglobal.SetMeterProvider(provider) meter.RecordBatch(context.Background(), nil, counter.Measurement(1)) require.EqualValues(t, []metrictest.Measured{ { Name: "test.counter", Library: metrictest.Library{ InstrumentationName: "builtin", }, Labels: metrictest.LabelsToMap(), Number: asInt(1), }, }, metrictest.AsStructs(provider.MeasurementBatches)) } golang-opentelemetry-otel-1.1.0/internal/metric/global/metric.go000066400000000000000000000035201414226744000247700ustar00rootroot00000000000000// 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 ( "sync" "sync/atomic" "go.opentelemetry.io/otel/metric" ) type meterProviderHolder struct { mp metric.MeterProvider } var ( globalMeter = defaultMeterValue() delegateMeterOnce sync.Once ) // MeterProvider is the internal implementation for global.MeterProvider. func MeterProvider() metric.MeterProvider { return globalMeter.Load().(meterProviderHolder).mp } // SetMeterProvider is the internal implementation for global.SetMeterProvider. func SetMeterProvider(mp metric.MeterProvider) { delegateMeterOnce.Do(func() { current := MeterProvider() if current == mp { // Setting the provider to the prior default is nonsense, panic. // Panic is acceptable because we are likely still early in the // process lifetime. panic("invalid MeterProvider, the global instance cannot be reinstalled") } else if def, ok := current.(*meterProvider); ok { def.setDelegate(mp) } }) globalMeter.Store(meterProviderHolder{mp: mp}) } func defaultMeterValue() *atomic.Value { v := &atomic.Value{} v.Store(meterProviderHolder{mp: newMeterProvider()}) return v } // ResetForTest restores the initial global state, for testing purposes. func ResetForTest() { globalMeter = defaultMeterValue() delegateMeterOnce = sync.Once{} } golang-opentelemetry-otel-1.1.0/internal/metric/global/registry_test.go000066400000000000000000000074731414226744000264270ustar00rootroot00000000000000// 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 ( "context" "errors" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/internal/metric/registry" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/sdkapi" ) type ( newFunc func(name, libraryName string) (sdkapi.InstrumentImpl, error) ) var ( allNew = map[string]newFunc{ "counter.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewInt64Counter(name)) }, "counter.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewFloat64Counter(name)) }, "histogram.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewInt64Histogram(name)) }, "histogram.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewFloat64Histogram(name)) }, "gauge.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "gauge.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) { return unwrap(MeterProvider().Meter(libraryName).NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) { if impl == nil { return nil, err } if s, ok := impl.(interface { SyncImpl() sdkapi.SyncImpl }); ok { return s.SyncImpl(), err } if a, ok := impl.(interface { AsyncImpl() sdkapi.AsyncImpl }); ok { return a.AsyncImpl(), err } return nil, err } func TestRegistrySameInstruments(t *testing.T) { for _, nf := range allNew { ResetForTest() inst1, err1 := nf("this", "meter") inst2, err2 := nf("this", "meter") require.NoError(t, err1) require.NoError(t, err2) require.Equal(t, inst1, inst2) SetMeterProvider(metrictest.NewMeterProvider()) require.Equal(t, inst1, inst2) } } func TestRegistryDifferentNamespace(t *testing.T) { for _, nf := range allNew { ResetForTest() inst1, err1 := nf("this", "meter1") inst2, err2 := nf("this", "meter2") require.NoError(t, err1) require.NoError(t, err2) if inst1.Descriptor().InstrumentKind().Synchronous() { // They're equal because of a `nil` pointer at this point. // (Only for synchronous instruments, which lack callacks.) require.EqualValues(t, inst1, inst2) } SetMeterProvider(metrictest.NewMeterProvider()) // They're different after the deferred setup. require.NotEqual(t, inst1, inst2) } } func TestRegistryDiffInstruments(t *testing.T) { for origName, origf := range allNew { ResetForTest() _, err := origf("this", "super") require.NoError(t, err) for newName, nf := range allNew { if newName == origName { continue } other, err := nf("this", "super") require.Error(t, err) require.NotNil(t, other) require.True(t, errors.Is(err, registry.ErrMetricKindMismatch)) require.Contains(t, err.Error(), "by this name with another kind or number type") } } } golang-opentelemetry-otel-1.1.0/internal/metric/global/state_test.go000066400000000000000000000023171414226744000256670ustar00rootroot00000000000000// 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_test import ( "testing" internalglobal "go.opentelemetry.io/otel/internal/metric/global" metricglobal "go.opentelemetry.io/otel/metric/global" ) func TestResetsOfGlobalsPanic(t *testing.T) { internalglobal.ResetForTest() tests := map[string]func(){ "SetMeterProvider": func() { metricglobal.SetMeterProvider(metricglobal.GetMeterProvider()) }, } for name, test := range tests { shouldPanic(t, name, test) } } func shouldPanic(t *testing.T, name string, f func()) { defer func() { if r := recover(); r == nil { t.Errorf("calling %s with default global did not panic", name) } }() f() } golang-opentelemetry-otel-1.1.0/internal/metric/go.mod000066400000000000000000000053531414226744000230320ustar00rootroot00000000000000module go.opentelemetry.io/otel/internal/metric go 1.15 require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 ) replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/internal/metric => ./ replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../tools replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/internal/metric/go.sum000066400000000000000000000025761414226744000230630ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/internal/metric/registry/000077500000000000000000000000001414226744000235665ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/metric/registry/doc.go000066400000000000000000000020531414226744000246620ustar00rootroot00000000000000// 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 registry provides a non-standalone implementation of MeterProvider that adds uniqueness checking for instrument descriptors on top of other MeterProvider it wraps. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. */ package registry // import "go.opentelemetry.io/otel/internal/metric/registry" golang-opentelemetry-otel-1.1.0/internal/metric/registry/registry.go000066400000000000000000000104501414226744000257650ustar00rootroot00000000000000// 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 registry // import "go.opentelemetry.io/otel/internal/metric/registry" import ( "context" "fmt" "sync" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/sdkapi" ) // UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding // uniqueness checking for instrument descriptors. type UniqueInstrumentMeterImpl struct { lock sync.Mutex impl sdkapi.MeterImpl state map[string]sdkapi.InstrumentImpl } var _ sdkapi.MeterImpl = (*UniqueInstrumentMeterImpl)(nil) // ErrMetricKindMismatch is the standard error for mismatched metric // instrument definitions. var ErrMetricKindMismatch = fmt.Errorf( "a metric was already registered by this name with another kind or number type") // NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl // with the addition of instrument name uniqueness checking. func NewUniqueInstrumentMeterImpl(impl sdkapi.MeterImpl) *UniqueInstrumentMeterImpl { return &UniqueInstrumentMeterImpl{ impl: impl, state: map[string]sdkapi.InstrumentImpl{}, } } // MeterImpl gives the caller access to the underlying MeterImpl // used by this UniqueInstrumentMeterImpl. func (u *UniqueInstrumentMeterImpl) MeterImpl() sdkapi.MeterImpl { return u.impl } // RecordBatch implements sdkapi.MeterImpl. func (u *UniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...sdkapi.Measurement) { u.impl.RecordBatch(ctx, labels, ms...) } // NewMetricKindMismatchError formats an error that describes a // mismatched metric instrument definition. func NewMetricKindMismatchError(desc sdkapi.Descriptor) error { return fmt.Errorf("metric %s registered as %s %s: %w", desc.Name(), desc.NumberKind(), desc.InstrumentKind(), ErrMetricKindMismatch) } // Compatible determines whether two sdkapi.Descriptors are considered // the same for the purpose of uniqueness checking. func Compatible(candidate, existing sdkapi.Descriptor) bool { return candidate.InstrumentKind() == existing.InstrumentKind() && candidate.NumberKind() == existing.NumberKind() } // checkUniqueness returns an ErrMetricKindMismatch error if there is // a conflict between a descriptor that was already registered and the // `descriptor` argument. If there is an existing compatible // registration, this returns the already-registered instrument. If // there is no conflict and no prior registration, returns (nil, nil). func (u *UniqueInstrumentMeterImpl) checkUniqueness(descriptor sdkapi.Descriptor) (sdkapi.InstrumentImpl, error) { impl, ok := u.state[descriptor.Name()] if !ok { return nil, nil } if !Compatible(descriptor, impl.Descriptor()) { return nil, NewMetricKindMismatchError(impl.Descriptor()) } return impl, nil } // NewSyncInstrument implements sdkapi.MeterImpl. func (u *UniqueInstrumentMeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) { u.lock.Lock() defer u.lock.Unlock() impl, err := u.checkUniqueness(descriptor) if err != nil { return nil, err } else if impl != nil { return impl.(sdkapi.SyncImpl), nil } syncInst, err := u.impl.NewSyncInstrument(descriptor) if err != nil { return nil, err } u.state[descriptor.Name()] = syncInst return syncInst, nil } // NewAsyncInstrument implements sdkapi.MeterImpl. func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument( descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner, ) (sdkapi.AsyncImpl, error) { u.lock.Lock() defer u.lock.Unlock() impl, err := u.checkUniqueness(descriptor) if err != nil { return nil, err } else if impl != nil { return impl.(sdkapi.AsyncImpl), nil } asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner) if err != nil { return nil, err } u.state[descriptor.Name()] = asyncInst return asyncInst, nil } golang-opentelemetry-otel-1.1.0/internal/metric/registry/registry_test.go000066400000000000000000000067761414226744000270440ustar00rootroot00000000000000// 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 registry_test import ( "context" "errors" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/internal/metric/registry" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/sdkapi" ) type ( newFunc func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) ) var ( allNew = map[string]newFunc{ "counter.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewInt64Counter(name)) }, "counter.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewFloat64Counter(name)) }, "histogram.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewInt64Histogram(name)) }, "histogram.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewFloat64Histogram(name)) }, "gaugeobserver.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {})) }, "gaugeobserver.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { return unwrap(m.NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {})) }, } ) func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) { if impl == nil { return nil, err } if s, ok := impl.(interface { SyncImpl() sdkapi.SyncImpl }); ok { return s.SyncImpl(), err } if a, ok := impl.(interface { AsyncImpl() sdkapi.AsyncImpl }); ok { return a.AsyncImpl(), err } return nil, err } func testMeterWithRegistry(name string) metric.Meter { return metric.WrapMeterImpl( registry.NewUniqueInstrumentMeterImpl( metrictest.NewMeterProvider().Meter(name).MeterImpl(), ), ) } func TestRegistrySameInstruments(t *testing.T) { for _, nf := range allNew { meter := testMeterWithRegistry("meter") inst1, err1 := nf(meter, "this") inst2, err2 := nf(meter, "this") require.NoError(t, err1) require.NoError(t, err2) require.Equal(t, inst1, inst2) } } func TestRegistryDifferentNamespace(t *testing.T) { for _, nf := range allNew { provider := metrictest.NewMeterProvider() meter1 := provider.Meter("meter1") meter2 := provider.Meter("meter2") inst1, err1 := nf(meter1, "this") inst2, err2 := nf(meter2, "this") require.NoError(t, err1) require.NoError(t, err2) require.NotEqual(t, inst1, inst2) } } func TestRegistryDiffInstruments(t *testing.T) { for origName, origf := range allNew { meter := testMeterWithRegistry("meter") _, err := origf(meter, "this") require.NoError(t, err) for newName, nf := range allNew { if newName == origName { continue } other, err := nf(meter, "this") require.Error(t, err) require.NotNil(t, other) require.True(t, errors.Is(err, registry.ErrMetricKindMismatch)) } } } golang-opentelemetry-otel-1.1.0/internal/rawhelpers.go000066400000000000000000000022351414226744000231400ustar00rootroot00000000000000// 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 ( "math" "unsafe" ) func BoolToRaw(b bool) uint64 { if b { return 1 } return 0 } func RawToBool(r uint64) bool { return r != 0 } func Int64ToRaw(i int64) uint64 { return uint64(i) } func RawToInt64(r uint64) int64 { return int64(r) } func Float64ToRaw(f float64) uint64 { return math.Float64bits(f) } func RawToFloat64(r uint64) float64 { return math.Float64frombits(r) } func RawPtrToFloat64Ptr(r *uint64) *float64 { return (*float64)(unsafe.Pointer(r)) } func RawPtrToInt64Ptr(r *uint64) *int64 { return (*int64)(unsafe.Pointer(r)) } golang-opentelemetry-otel-1.1.0/internal/tools/000077500000000000000000000000001414226744000215735ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/tools/common.go000066400000000000000000000034471414226744000234220ustar00rootroot00000000000000// 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 tools provides helper functions used in scripts within the // internal/tools module, as well as imports needed for a build with the // "tools" build tag. package tools import ( "errors" "fmt" "os" "path/filepath" "strings" ) // FindRepoRoot retrieves the root of the repository containing the current working directory. // Beginning at the current working directory (dir), the algorithm checks if joining the ".git" // suffix, such as "dir.get", is a valid file. Otherwise, it will continue checking the dir's // parent directory until it reaches the repo root or returns an error if it cannot be found. func FindRepoRoot() (string, error) { start, err := os.Getwd() if err != nil { return "", err } dir := start for { _, err := os.Stat(filepath.Join(dir, ".git")) if errors.Is(err, os.ErrNotExist) { dir = filepath.Dir(dir) // From https://golang.org/pkg/path/filepath/#Dir: // The returned path does not end in a separator unless it is the root directory. if strings.HasSuffix(dir, string(filepath.Separator)) { return "", fmt.Errorf("unable to find git repository enclosing working dir %s", start) } continue } if err != nil { return "", err } return dir, nil } } golang-opentelemetry-otel-1.1.0/internal/tools/crosslink/000077500000000000000000000000001414226744000236025ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/tools/crosslink/crosslink.go000066400000000000000000000067421414226744000261510ustar00rootroot00000000000000// 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. // The crosslink tool generates and maintains replace directives in all // the go.mod files within this repository. Some directives are superfluous // (e.g. because the replaced module doesn't occur in the dependency tree), // but we generate them anyway for the sake of consistency (#1529 tracks // pruning this to a mininal set). // // In particular, we generate a replace directive from each module to itself // (i.e., the target path "./"). This is actually necessary in the presence of // cyclic dependencies between modules. package main import ( "encoding/json" "errors" "fmt" "io" "log" "os" "os/exec" "path/filepath" "strings" "text/tabwriter" "go.opentelemetry.io/otel/internal/tools" ) type repo string type mod struct { filePath string importPath string } func (r repo) findModules() (mods, error) { var results []mod err := filepath.Walk(string(r), func(path string, info os.FileInfo, err error) error { if !info.IsDir() { return nil } _, err = os.Stat(filepath.Join(path, "go.mod")) if errors.Is(err, os.ErrNotExist) { return nil } if err != nil { return err } cmd := exec.Command("go", "mod", "edit", "-json") cmd.Dir = path out, err := cmd.Output() if err != nil { return err } var result struct { Module struct { Path string } } err = json.Unmarshal(out, &result) if err != nil { return err } results = append(results, mod{ filePath: path, importPath: result.Module.Path, }) return nil }) return results, err } type mods []mod func (m mods) print(w io.Writer) error { tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) if _, err := fmt.Fprintln(tw, "FILE PATH\tIMPORT PATH"); err != nil { return err } for _, m := range m { if _, err := fmt.Fprintf(tw, "%s\t%s\n", m.filePath, m.importPath); err != nil { return err } } return tw.Flush() } func (m mods) crossLink() error { for _, from := range m { args := []string{"mod", "edit"} for _, to := range m { localPath, err := filepath.Rel(from.filePath, to.filePath) if err != nil { return err } if localPath == "." || localPath == ".." { localPath += "/" } else if !strings.HasPrefix(localPath, "..") { localPath = "./" + localPath } args = append(args, "-replace", to.importPath+"="+localPath) } cmd := exec.Command("go", args...) cmd.Dir = from.filePath out, err := cmd.CombinedOutput() if err != nil { log.Println(string(out)) return err } } return nil } func main() { repoRootStr, err := tools.FindRepoRoot() if err != nil { log.Fatalf("unable to find repo root: %v", err) } repoRoot := repo(repoRootStr) mods, err := repoRoot.findModules() if err != nil { log.Fatalf("unable to list modules: %v", err) } if err := mods.print(os.Stdout); err != nil { log.Fatalf("unable to print modules: %v", err) } if err := mods.crossLink(); err != nil { log.Fatalf("unable to crosslink: %v", err) } } golang-opentelemetry-otel-1.1.0/internal/tools/go.mod000066400000000000000000000061151414226744000227040ustar00rootroot00000000000000module go.opentelemetry.io/otel/internal/tools go 1.15 require ( github.com/client9/misspell v0.3.4 github.com/gogo/protobuf v1.3.2 github.com/golangci/golangci-lint v1.42.1 github.com/itchyny/gojq v0.12.5 github.com/jcchavezs/porto v0.3.0 github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad go.opentelemetry.io/build-tools/multimod v0.0.0-20210920164323-2ceabab23375 go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210920164323-2ceabab23375 golang.org/x/mod v0.5.1 // indirect golang.org/x/tools v0.1.7 ) replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ./ replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/internal/tools/go.sum000066400000000000000000003663211414226744000227410ustar00rootroot000000000000004d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a h1:wFEQiK85fRsEVF0CRrPAos5LoAryUsIX1kPW/WrIqFw= 4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Antonboom/errname v0.1.4 h1:lGSlI42Gm4bI1e+IITtXJXvxFM8N7naWimVFKcb0McY= github.com/Antonboom/errname v0.1.4/go.mod h1:jRXo3m0E0EuCnK3wbsSVH3X55Z4iTDLl6ZfCxwFj4TM= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ashanbrown/forbidigo v1.2.0 h1:RMlEFupPCxQ1IogYOQUnIQwGEUGK8g5vAPMRyJoSxbc= github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde h1:YOsoVXsZQPA9aOTy1g0lAJv5VzZUvwQuZqug8XPeqfM= github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.8 h1:cnZrThioNW9gSV5JsRIXmkyHUbcDH7Y9hkzFDVc9/j0= github.com/charithe/durationcheck v0.0.8/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI= github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daixiang0/gci v0.2.9 h1:iwJvwQpBZmMg31w+QQ6jsyZ54KEATn6/nfARbBNW294= github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 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.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.2 h1:K5s1W2fGfkoWXsFlxBNqT6J0ZCncPaKrGM5qe0bni68= github.com/esimonov/ifshort v1.0.2/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-critic/go-critic v0.5.6 h1:siUR1+322iVikWXoV75I1YRfNaC/yaLzhdF9Zwd8Tus= github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg= github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk= github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= github.com/golangci/golangci-lint v1.42.1 h1:nC4WyrbdnNdohDVUoNKjy/4N4FTM1gCFaVeXecy6vzM= github.com/golangci/golangci-lint v1.42.1/go.mod h1:MuInrVlgg2jq4do6XI1jbkErbVHVbwdrLLtGv6p2wPI= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo= github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5 h1:c9Mqqrm/Clj5biNaG7rABrmwUq88nHh0uABo2b/WYmc= github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254 h1:Nb2aRlC404yz7gQIfRZxX9/MLvQiqXyiBTJtgAy6yrI= github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= github.com/gostaticanalysis/analysisutil v0.4.1 h1:/7clKqrVfiVwiBQLM0Uke4KvXnO6JcCTS7HwF2D6wG8= github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= github.com/gostaticanalysis/comment v1.4.1 h1:xHopR5L2lRz6OsjH4R2HG5wRhW9ySl3FsHIvi5pcXwc= github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5 h1:rx8127mFPqXXsfPSo8BwnIU97MKFZc89WHAHt8PwDVY= github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/go-flags v1.5.0 h1:Z5q2ist2sfDjDlExVPBrMqlsEDxDR2h4zuOElB0OEYI= github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= github.com/itchyny/gojq v0.12.5 h1:6SJ1BQ1VAwJAlIvLSIZmqHP/RUEq3qfVWvsRxrqhsD0= github.com/itchyny/gojq v0.12.5/go.mod h1:3e1hZXv+Kwvdp6V9HXpVrvddiHVApi5EDZwS+zLFeiE= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcchavezs/porto v0.3.0 h1:JSKeMsqexngzHUpiv4NPPADSNBF9bDyavGRDWedzNeM= github.com/jcchavezs/porto v0.3.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.0 h1:u6h4eiNuCLqk73Ic5TXQq9yZS+uEXTdusn7c3w1Mr6A= github.com/jingyugao/rowserrcheck v1.1.0/go.mod h1:TOQpc2SLx6huPfoFGK3UOnEG+u02D3C1GeosjupAKCA= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d h1:XeSMXURZPtUffuWAaq90o6kLgZdgu+QA8wk4MPC8ikI= github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY= github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.4.0 h1:2Nx7XbdbE/BYZeoip2mURKUdtHQRuy6Ug+wR7K9ywNM= github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= github.com/kunwardeep/paralleltest v1.0.2 h1:/jJRv0TiqPoEy/Y8dQxCFJhD56uS/pnvtatgTZBHokU= github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M= github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/ldez/gomoddirectives v0.2.2 h1:p9/sXuNFArS2RLc+UpYZSI4KQwGMEDWC/LbtF5OPFVg= github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= github.com/ldez/tagliatelle v0.2.0 h1:693V8Bf1NdShJ8eu/s84QySA0J2VWBanVBa2WwXD/Wk= github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 h1:QASJXOGm2RZ5Ardbc86qNFvby9AqkLDibfChMtAg5QM= github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.1.1 h1:mkXNHP14Y6tfq+ocnQaiKEtgJDM41yaoyQq4qn6TD/4= github.com/mgechev/revive v1.1.1/go.mod h1:PKqk4L74K6wVNwY2b6fr+9Qqr/3hIsHVfZCJdbvozrY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaPw= github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.2.3 h1:+ANTMqRNrqwInnP9aszg/0jDo+zbXa4x66U19Bx/oTk= github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= github.com/nishanths/predeclared v0.2.1 h1:1TXtjmy4f3YCFjTxRd8zcFHOmoUir+gp0ESzjFzG2sw= github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349 h1:Kq/3kL0k033ds3tyez5lFPrfQ74fNJ+OqCclRipubwA= github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= github.com/quasilyte/go-ruleguard v0.3.4 h1:F6l5p6+7WBcTKS7foNQ4wqA39zjn2+RbdbyzGxIq1B0= github.com/quasilyte/go-ruleguard v0.3.4/go.mod h1:57FZgMnoo6jqxkYKmVj5Fc8vOt0rVzoE/UNAmFFIPqA= github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8= github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA= github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/securego/gosec/v2 v2.8.1 h1:Tyy/nsH39TYCOkqf5HAgRE+7B5D8sHDwPdXRgFWokh8= github.com/securego/gosec/v2 v2.8.1/go.mod h1:pUmsq6+VyFEElJMUX+QB3p3LWNHXg1R3xh2ssVJPs8Q= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/ssgreg/nlreturn/v2 v2.1.0 h1:6/s4Rc49L6Uo6RLjhWZGBpWWjfzk2yrf1nIW8m4wgVA= github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b h1:HxLVTlqcHhFAz3nWUcuvpH7WuOMv8LQoCWmruLfFH2U= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tetafro/godot v1.4.9 h1:wsNd0RuUxISqqudFqchsSsMqsM188DoZVPBeKl87tP0= github.com/tetafro/godot v1.4.9/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4= github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tomarrell/wrapcheck/v2 v2.3.0 h1:i3DNjtyyL1xwaBQOsPPk8LAcpayWfQv2rxNi9b/eEx4= github.com/tomarrell/wrapcheck/v2 v2.3.0/go.mod h1:aF5rnkdtqNWP/gC7vPUO5pKsB0Oac2FDTQP4F+dpZMU= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.4.0 h1:1t0f8Uiaq+fqKteUR4N9Umr6E99R+lDnLnq7PwX2PPE= github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/uudashr/gocognit v1.0.5 h1:rrSex7oHr3/pPLQ0xoWq108XMU8s678FJcQ+aSfOHa4= github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad h1:W0LEBv82YCGEtcmPA3uNZBI33/qF//HAAs3MawDjRa0= github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+MXnS6EwGElrSRjUzQDLXreJlzYLlWiHtt8hM= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yeya24/promlinter v0.1.0 h1:goWULN0jH5Yajmu/K+v1xCqIREeB+48OiJ2uu2ssc7U= github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 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= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 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.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/build-tools v0.0.0-20210719163622-92017e64f35b h1:tFMjUqEDGM2F82663yYidqTluwEJmmihk/AXr19J7rI= go.opentelemetry.io/build-tools v0.0.0-20210719163622-92017e64f35b/go.mod h1:zZRrJN8qdwDdPNkCEyww4SW54mM1Da0v9H3TyQet9T4= go.opentelemetry.io/build-tools/multimod v0.0.0-20210920164323-2ceabab23375 h1:5zVDNFcOwiMee9Qm8sH08iw+9cJZk+l/Y3mVa2D/zmM= go.opentelemetry.io/build-tools/multimod v0.0.0-20210920164323-2ceabab23375/go.mod h1:mPh1L/tfTGyVNnSQOTlTSi2CBpci13Ft8jE4Glik2es= go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210920164323-2ceabab23375 h1:EaeArFokiObXc/jkX0Ck3u2d6lWDmeWdEPy2IdlF6iw= go.opentelemetry.io/build-tools/semconvgen v0.0.0-20210920164323-2ceabab23375/go.mod h1:QJyAzHKDKGDl52AUIAZhWKx3nOPBZJ3dD44utso2FPE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 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-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= 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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.2.1 h1:/EPr//+UMMXwMTkXvCCoaJDq8cpjMO80Ou+L4PDo2mY= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 h1:HT3e4Krq+IE44tiN36RvVEb6tvqeIdtsVSsxmNPqlFU= mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= golang-opentelemetry-otel-1.1.0/internal/tools/tools.go000066400000000000000000000020261414226744000232620ustar00rootroot00000000000000// 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. //go:build tools // +build tools package tools import ( _ "github.com/client9/misspell/cmd/misspell" _ "github.com/gogo/protobuf/protoc-gen-gogofast" _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/itchyny/gojq" _ "github.com/jcchavezs/porto/cmd/porto" _ "github.com/wadey/gocovmerge" _ "go.opentelemetry.io/build-tools/multimod" _ "go.opentelemetry.io/build-tools/semconvgen" _ "golang.org/x/tools/cmd/stringer" ) golang-opentelemetry-otel-1.1.0/internal/trace/000077500000000000000000000000001414226744000215315ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/trace/noop/000077500000000000000000000000001414226744000225045ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/internal/trace/noop/noop.go000066400000000000000000000017331414226744000240120ustar00rootroot00000000000000// 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 noop provides noop tracing implementations for tracer and span. package noop import ( "context" "go.opentelemetry.io/otel/trace" ) var ( // Tracer is a noop tracer that starts noop spans. Tracer trace.Tracer // Span is a noop Span. Span trace.Span ) func init() { Tracer = trace.NewNoopTracerProvider().Tracer("") _, Span = Tracer.Start(context.Background(), "") } golang-opentelemetry-otel-1.1.0/metric/000077500000000000000000000000001414226744000201025ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/config.go000066400000000000000000000066631414226744000217110ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/metric" import ( "go.opentelemetry.io/otel/metric/unit" ) // InstrumentConfig contains options for metric instrument descriptors. type InstrumentConfig struct { description string unit unit.Unit } // Description describes the instrument in human-readable terms. func (cfg InstrumentConfig) Description() string { return cfg.description } // Unit describes the measurement unit for a instrument. func (cfg InstrumentConfig) Unit() unit.Unit { return cfg.unit } // InstrumentOption is an interface for applying metric instrument options. type InstrumentOption interface { // ApplyMeter is used to set a InstrumentOption value of a // InstrumentConfig. applyInstrument(*InstrumentConfig) } // NewInstrumentConfig creates a new InstrumentConfig // and applies all the given options. func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig { var config InstrumentConfig for _, o := range opts { o.applyInstrument(&config) } return config } type instrumentOptionFunc func(*InstrumentConfig) func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) { fn(cfg) } // WithDescription applies provided description. func WithDescription(desc string) InstrumentOption { return instrumentOptionFunc(func(cfg *InstrumentConfig) { cfg.description = desc }) } // WithUnit applies provided unit. func WithUnit(unit unit.Unit) InstrumentOption { return instrumentOptionFunc(func(cfg *InstrumentConfig) { cfg.unit = unit }) } // MeterConfig contains options for Meters. type MeterConfig struct { instrumentationVersion string schemaURL string } // InstrumentationVersion is the version of the library providing instrumentation. func (cfg MeterConfig) InstrumentationVersion() string { return cfg.instrumentationVersion } // SchemaURL is the schema_url of the library providing instrumentation. func (cfg MeterConfig) SchemaURL() string { return cfg.schemaURL } // MeterOption is an interface for applying Meter options. type MeterOption interface { // ApplyMeter is used to set a MeterOption value of a MeterConfig. applyMeter(*MeterConfig) } // NewMeterConfig creates a new MeterConfig and applies // all the given options. func NewMeterConfig(opts ...MeterOption) MeterConfig { var config MeterConfig for _, o := range opts { o.applyMeter(&config) } return config } type meterOptionFunc func(*MeterConfig) func (fn meterOptionFunc) applyMeter(cfg *MeterConfig) { fn(cfg) } // WithInstrumentationVersion sets the instrumentation version. func WithInstrumentationVersion(version string) MeterOption { return meterOptionFunc(func(config *MeterConfig) { config.instrumentationVersion = version }) } // WithSchemaURL sets the schema URL. func WithSchemaURL(schemaURL string) MeterOption { return meterOptionFunc(func(config *MeterConfig) { config.schemaURL = schemaURL }) } golang-opentelemetry-otel-1.1.0/metric/doc.go000066400000000000000000000054301414226744000212000ustar00rootroot00000000000000// 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 metric provides an implementation of the metrics part of the OpenTelemetry API. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. Measurements can be made about an operation being performed or the state of a system in general. These measurements can be crucial to the reliable operation of code and provide valuable insights about the inner workings of a system. Measurements are made using instruments provided by this package. The type of instrument used will depend on the type of measurement being made and of what part of a system is being measured. Instruments are categorized as Synchronous or Asynchronous and independently as Adding or Grouping. Synchronous instruments are called by the user with a Context. Asynchronous instruments are called by the SDK during collection. Adding instruments are semantically intended for capturing a sum. Grouping instruments are intended for capturing a distribution. Adding instruments may be monotonic, in which case they are non-decreasing and naturally define a rate. The synchronous instrument names are: Counter: adding, monotonic UpDownCounter: adding Histogram: grouping and the asynchronous instruments are: CounterObserver: adding, monotonic UpDownCounterObserver: adding GaugeObserver: grouping All instruments are provided with support for either float64 or int64 input values. An instrument is created using a Meter. Additionally, a Meter is used to record batches of synchronous measurements or asynchronous observations. A Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to the instrumentation it instruments and must be named and versioned when created with a MeterProvider with the name and version of the instrumentation library. Instrumentation should be designed to accept a MeterProvider from which it can create its own unique Meter. Alternatively, the registered global MeterProvider from the go.opentelemetry.io/otel package can be used as a default. */ package metric // import "go.opentelemetry.io/otel/metric" golang-opentelemetry-otel-1.1.0/metric/global/000077500000000000000000000000001414226744000213425ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/global/metric.go000066400000000000000000000036511414226744000231610ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/metric/global" import ( "go.opentelemetry.io/otel/internal/metric/global" "go.opentelemetry.io/otel/metric" ) // Meter creates an implementation of the Meter interface from the global // MeterProvider. The instrumentationName must be the name of the library // providing instrumentation. This name may be the same as the instrumented // code only if that code provides built-in instrumentation. If the // instrumentationName is empty, then a implementation defined default name // will be used instead. // // This is short for MeterProvider().Meter(name) func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { return GetMeterProvider().Meter(instrumentationName, opts...) } // GetMeterProvider returns the registered global meter provider. If // none is registered then a default meter provider is returned that // forwards the Meter interface to the first registered Meter. // // Use the meter provider to create a named meter. E.g. // meter := global.MeterProvider().Meter("example.com/foo") // or // meter := global.Meter("example.com/foo") func GetMeterProvider() metric.MeterProvider { return global.MeterProvider() } // SetMeterProvider registers `mp` as the global meter provider. func SetMeterProvider(mp metric.MeterProvider) { global.SetMeterProvider(mp) } golang-opentelemetry-otel-1.1.0/metric/global/metric_test.go000066400000000000000000000021741414226744000242170ustar00rootroot00000000000000// 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 ( "testing" "go.opentelemetry.io/otel/metric" ) type testMeterProvider struct{} var _ metric.MeterProvider = &testMeterProvider{} func (*testMeterProvider) Meter(_ string, _ ...metric.MeterOption) metric.Meter { return metric.Meter{} } func TestMultipleGlobalMeterProvider(t *testing.T) { p1 := testMeterProvider{} p2 := metric.NewNoopMeterProvider() SetMeterProvider(&p1) SetMeterProvider(p2) got := GetMeterProvider() want := p2 if got != want { t.Fatalf("MeterProvider: got %p, want %p\n", got, want) } } golang-opentelemetry-otel-1.1.0/metric/go.mod000066400000000000000000000053051414226744000212130ustar00rootroot00000000000000module go.opentelemetry.io/otel/metric go 1.15 replace go.opentelemetry.io/otel => ../ replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../internal/tools replace go.opentelemetry.io/otel/metric => ./ replace go.opentelemetry.io/otel/sdk => ../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric replace go.opentelemetry.io/otel/trace => ../trace require ( github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/internal/metric v0.24.0 ) replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../example/fib replace go.opentelemetry.io/otel/schema => ../schema golang-opentelemetry-otel-1.1.0/metric/go.sum000066400000000000000000000025761414226744000212470ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/metric/metric.go000066400000000000000000000537211414226744000217240ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) // MeterProvider supports named Meter instances. type MeterProvider interface { // Meter creates an implementation of the Meter interface. // The instrumentationName must be the name of the library providing // instrumentation. This name may be the same as the instrumented code // only if that code provides built-in instrumentation. If the // instrumentationName is empty, then a implementation defined default // name will be used instead. Meter(instrumentationName string, opts ...MeterOption) Meter } // Meter is the creator of metric instruments. // // An uninitialized Meter is a no-op implementation. type Meter struct { impl sdkapi.MeterImpl } // WrapMeterImpl constructs a `Meter` implementation from a // `MeterImpl` implementation. func WrapMeterImpl(impl sdkapi.MeterImpl) Meter { return Meter{ impl: impl, } } // Measurement is used for reporting a synchronous batch of metric // values. Instances of this type should be created by synchronous // instruments (e.g., Int64Counter.Measurement()). // // Note: This is an alias because it is a first-class member of the // API but is also part of the lower-level sdkapi interface. type Measurement = sdkapi.Measurement // Observation is used for reporting an asynchronous batch of metric // values. Instances of this type should be created by asynchronous // instruments (e.g., Int64GaugeObserver.Observation()). // // Note: This is an alias because it is a first-class member of the // API but is also part of the lower-level sdkapi interface. type Observation = sdkapi.Observation // RecordBatch atomically records a batch of measurements. func (m Meter) RecordBatch(ctx context.Context, ls []attribute.KeyValue, ms ...Measurement) { if m.impl == nil { return } m.impl.RecordBatch(ctx, ls, ms...) } // NewBatchObserver creates a new BatchObserver that supports // making batches of observations for multiple instruments. func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver { return BatchObserver{ meter: m, runner: newBatchAsyncRunner(callback), } } // NewInt64Counter creates a new integer Counter instrument with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) { return wrapInt64CounterInstrument( m.newSync(name, sdkapi.CounterInstrumentKind, number.Int64Kind, options)) } // NewFloat64Counter creates a new floating point Counter with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) { return wrapFloat64CounterInstrument( m.newSync(name, sdkapi.CounterInstrumentKind, number.Float64Kind, options)) } // NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) { return wrapInt64UpDownCounterInstrument( m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Int64Kind, options)) } // NewFloat64UpDownCounter creates a new floating point UpDownCounter with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) { return wrapFloat64UpDownCounterInstrument( m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Float64Kind, options)) } // NewInt64Histogram creates a new integer Histogram instrument with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewInt64Histogram(name string, opts ...InstrumentOption) (Int64Histogram, error) { return wrapInt64HistogramInstrument( m.newSync(name, sdkapi.HistogramInstrumentKind, number.Int64Kind, opts)) } // NewFloat64Histogram creates a new floating point Histogram with the // given name, customized with options. May return an error if the // name is invalid (e.g., empty) or improperly registered (e.g., // duplicate registration). func (m Meter) NewFloat64Histogram(name string, opts ...InstrumentOption) (Float64Histogram, error) { return wrapFloat64HistogramInstrument( m.newSync(name, sdkapi.HistogramInstrumentKind, number.Float64Kind, opts)) } // NewInt64GaugeObserver creates a new integer GaugeObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64GaugeObserver, error) { if callback == nil { return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64GaugeObserverInstrument( m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts, newInt64AsyncRunner(callback))) } // NewFloat64GaugeObserver creates a new floating point GaugeObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64GaugeObserver, error) { if callback == nil { return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64GaugeObserverInstrument( m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts, newFloat64AsyncRunner(callback))) } // NewInt64CounterObserver creates a new integer CounterObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewInt64CounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64CounterObserver, error) { if callback == nil { return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64CounterObserverInstrument( m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts, newInt64AsyncRunner(callback))) } // NewFloat64CounterObserver creates a new floating point CounterObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64CounterObserver, error) { if callback == nil { return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64CounterObserverInstrument( m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts, newFloat64AsyncRunner(callback))) } // NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument // with the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) { if callback == nil { return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64UpDownCounterObserverInstrument( m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts, newInt64AsyncRunner(callback))) } // NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with // the given name, running a given callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (m Meter) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) { if callback == nil { return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64UpDownCounterObserverInstrument( m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts, newFloat64AsyncRunner(callback))) } // NewInt64GaugeObserver creates a new integer GaugeObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewInt64GaugeObserver(name string, opts ...InstrumentOption) (Int64GaugeObserver, error) { if b.runner == nil { return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64GaugeObserverInstrument( b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts, b.runner)) } // NewFloat64GaugeObserver creates a new floating point GaugeObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewFloat64GaugeObserver(name string, opts ...InstrumentOption) (Float64GaugeObserver, error) { if b.runner == nil { return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64GaugeObserverInstrument( b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts, b.runner)) } // NewInt64CounterObserver creates a new integer CounterObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewInt64CounterObserver(name string, opts ...InstrumentOption) (Int64CounterObserver, error) { if b.runner == nil { return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64CounterObserverInstrument( b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts, b.runner)) } // NewFloat64CounterObserver creates a new floating point CounterObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewFloat64CounterObserver(name string, opts ...InstrumentOption) (Float64CounterObserver, error) { if b.runner == nil { return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64CounterObserverInstrument( b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts, b.runner)) } // NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument // with the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewInt64UpDownCounterObserver(name string, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) { if b.runner == nil { return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapInt64UpDownCounterObserverInstrument( b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts, b.runner)) } // NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with // the given name, running in a batch callback, and customized with // options. May return an error if the name is invalid (e.g., empty) // or improperly registered (e.g., duplicate registration). func (b BatchObserver) NewFloat64UpDownCounterObserver(name string, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) { if b.runner == nil { return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil) } return wrapFloat64UpDownCounterObserverInstrument( b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts, b.runner)) } // MeterImpl returns the underlying MeterImpl of this Meter. func (m Meter) MeterImpl() sdkapi.MeterImpl { return m.impl } // newAsync constructs one new asynchronous instrument. func (m Meter) newAsync( name string, mkind sdkapi.InstrumentKind, nkind number.Kind, opts []InstrumentOption, runner sdkapi.AsyncRunner, ) ( sdkapi.AsyncImpl, error, ) { if m.impl == nil { return sdkapi.NewNoopAsyncInstrument(), nil } cfg := NewInstrumentConfig(opts...) desc := sdkapi.NewDescriptor(name, mkind, nkind, cfg.description, cfg.unit) return m.impl.NewAsyncInstrument(desc, runner) } // newSync constructs one new synchronous instrument. func (m Meter) newSync( name string, metricKind sdkapi.InstrumentKind, numberKind number.Kind, opts []InstrumentOption, ) ( sdkapi.SyncImpl, error, ) { if m.impl == nil { return sdkapi.NewNoopSyncInstrument(), nil } cfg := NewInstrumentConfig(opts...) desc := sdkapi.NewDescriptor(name, metricKind, numberKind, cfg.description, cfg.unit) return m.impl.NewSyncInstrument(desc) } // MeterMust is a wrapper for Meter interfaces that panics when any // instrument constructor encounters an error. type MeterMust struct { meter Meter } // BatchObserverMust is a wrapper for BatchObserver that panics when // any instrument constructor encounters an error. type BatchObserverMust struct { batch BatchObserver } // Must constructs a MeterMust implementation from a Meter, allowing // the application to panic when any instrument constructor yields an // error. func Must(meter Meter) MeterMust { return MeterMust{meter: meter} } // NewInt64Counter calls `Meter.NewInt64Counter` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter { if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil { panic(err) } else { return inst } } // NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter { if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil { panic(err) } else { return inst } } // NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter { if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil { panic(err) } else { return inst } } // NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter { if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil { panic(err) } else { return inst } } // NewInt64Histogram calls `Meter.NewInt64Histogram` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64Histogram(name string, mos ...InstrumentOption) Int64Histogram { if inst, err := mm.meter.NewInt64Histogram(name, mos...); err != nil { panic(err) } else { return inst } } // NewFloat64Histogram calls `Meter.NewFloat64Histogram` and returns the // instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64Histogram(name string, mos ...InstrumentOption) Float64Histogram { if inst, err := mm.meter.NewFloat64Histogram(name, mos...); err != nil { panic(err) } else { return inst } } // NewInt64GaugeObserver calls `Meter.NewInt64GaugeObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64GaugeObserver { if inst, err := mm.meter.NewInt64GaugeObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64GaugeObserver calls `Meter.NewFloat64GaugeObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64GaugeObserver { if inst, err := mm.meter.NewFloat64GaugeObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewInt64CounterObserver calls `Meter.NewInt64CounterObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64CounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64CounterObserver { if inst, err := mm.meter.NewInt64CounterObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64CounterObserver calls `Meter.NewFloat64CounterObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64CounterObserver { if inst, err := mm.meter.NewFloat64CounterObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewInt64UpDownCounterObserver calls `Meter.NewInt64UpDownCounterObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownCounterObserver { if inst, err := mm.meter.NewInt64UpDownCounterObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64UpDownCounterObserver calls `Meter.NewFloat64UpDownCounterObserver` and // returns the instrument, panicking if it encounters an error. func (mm MeterMust) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownCounterObserver { if inst, err := mm.meter.NewFloat64UpDownCounterObserver(name, callback, oos...); err != nil { panic(err) } else { return inst } } // NewBatchObserver returns a wrapper around BatchObserver that panics // when any instrument constructor returns an error. func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust { return BatchObserverMust{ batch: mm.meter.NewBatchObserver(callback), } } // NewInt64GaugeObserver calls `BatchObserver.NewInt64GaugeObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewInt64GaugeObserver(name string, oos ...InstrumentOption) Int64GaugeObserver { if inst, err := bm.batch.NewInt64GaugeObserver(name, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64GaugeObserver calls `BatchObserver.NewFloat64GaugeObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewFloat64GaugeObserver(name string, oos ...InstrumentOption) Float64GaugeObserver { if inst, err := bm.batch.NewFloat64GaugeObserver(name, oos...); err != nil { panic(err) } else { return inst } } // NewInt64CounterObserver calls `BatchObserver.NewInt64CounterObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewInt64CounterObserver(name string, oos ...InstrumentOption) Int64CounterObserver { if inst, err := bm.batch.NewInt64CounterObserver(name, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64CounterObserver calls `BatchObserver.NewFloat64CounterObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewFloat64CounterObserver(name string, oos ...InstrumentOption) Float64CounterObserver { if inst, err := bm.batch.NewFloat64CounterObserver(name, oos...); err != nil { panic(err) } else { return inst } } // NewInt64UpDownCounterObserver calls `BatchObserver.NewInt64UpDownCounterObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewInt64UpDownCounterObserver(name string, oos ...InstrumentOption) Int64UpDownCounterObserver { if inst, err := bm.batch.NewInt64UpDownCounterObserver(name, oos...); err != nil { panic(err) } else { return inst } } // NewFloat64UpDownCounterObserver calls `BatchObserver.NewFloat64UpDownCounterObserver` and // returns the instrument, panicking if it encounters an error. func (bm BatchObserverMust) NewFloat64UpDownCounterObserver(name string, oos ...InstrumentOption) Float64UpDownCounterObserver { if inst, err := bm.batch.NewFloat64UpDownCounterObserver(name, oos...); err != nil { panic(err) } else { return inst } } golang-opentelemetry-otel-1.1.0/metric/metric_instrument.go000066400000000000000000000531061414226744000242110ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/metric" import ( "context" "errors" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) // ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil. var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation") // Int64ObserverFunc is a type of callback that integral // observers run. type Int64ObserverFunc func(context.Context, Int64ObserverResult) // Float64ObserverFunc is a type of callback that floating point // observers run. type Float64ObserverFunc func(context.Context, Float64ObserverResult) // BatchObserverFunc is a callback argument for use with any // Observer instrument that will be reported as a batch of // observations. type BatchObserverFunc func(context.Context, BatchObserverResult) // Int64ObserverResult is passed to an observer callback to capture // observations for one asynchronous integer metric instrument. type Int64ObserverResult struct { instrument sdkapi.AsyncImpl function func([]attribute.KeyValue, ...Observation) } // Float64ObserverResult is passed to an observer callback to capture // observations for one asynchronous floating point metric instrument. type Float64ObserverResult struct { instrument sdkapi.AsyncImpl function func([]attribute.KeyValue, ...Observation) } // BatchObserverResult is passed to a batch observer callback to // capture observations for multiple asynchronous instruments. type BatchObserverResult struct { function func([]attribute.KeyValue, ...Observation) } // Observe captures a single integer value from the associated // instrument callback, with the given labels. func (ir Int64ObserverResult) Observe(value int64, labels ...attribute.KeyValue) { ir.function(labels, sdkapi.NewObservation(ir.instrument, number.NewInt64Number(value))) } // Observe captures a single floating point value from the associated // instrument callback, with the given labels. func (fr Float64ObserverResult) Observe(value float64, labels ...attribute.KeyValue) { fr.function(labels, sdkapi.NewObservation(fr.instrument, number.NewFloat64Number(value))) } // Observe captures a multiple observations from the associated batch // instrument callback, with the given labels. func (br BatchObserverResult) Observe(labels []attribute.KeyValue, obs ...Observation) { br.function(labels, obs...) } var _ sdkapi.AsyncSingleRunner = (*Int64ObserverFunc)(nil) var _ sdkapi.AsyncSingleRunner = (*Float64ObserverFunc)(nil) var _ sdkapi.AsyncBatchRunner = (*BatchObserverFunc)(nil) // newInt64AsyncRunner returns a single-observer callback for integer Observer instruments. func newInt64AsyncRunner(c Int64ObserverFunc) sdkapi.AsyncSingleRunner { return &c } // newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments. func newFloat64AsyncRunner(c Float64ObserverFunc) sdkapi.AsyncSingleRunner { return &c } // newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments. func newBatchAsyncRunner(c BatchObserverFunc) sdkapi.AsyncBatchRunner { return &c } // AnyRunner implements AsyncRunner. func (*Int64ObserverFunc) AnyRunner() {} // AnyRunner implements AsyncRunner. func (*Float64ObserverFunc) AnyRunner() {} // AnyRunner implements AsyncRunner. func (*BatchObserverFunc) AnyRunner() {} // Run implements AsyncSingleRunner. func (i *Int64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) { (*i)(ctx, Int64ObserverResult{ instrument: impl, function: function, }) } // Run implements AsyncSingleRunner. func (f *Float64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) { (*f)(ctx, Float64ObserverResult{ instrument: impl, function: function, }) } // Run implements AsyncBatchRunner. func (b *BatchObserverFunc) Run(ctx context.Context, function func([]attribute.KeyValue, ...Observation)) { (*b)(ctx, BatchObserverResult{ function: function, }) } // wrapInt64GaugeObserverInstrument converts an AsyncImpl into Int64GaugeObserver. func wrapInt64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64GaugeObserver, error) { common, err := checkNewAsync(asyncInst, err) return Int64GaugeObserver{asyncInstrument: common}, err } // wrapFloat64GaugeObserverInstrument converts an AsyncImpl into Float64GaugeObserver. func wrapFloat64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64GaugeObserver, error) { common, err := checkNewAsync(asyncInst, err) return Float64GaugeObserver{asyncInstrument: common}, err } // wrapInt64CounterObserverInstrument converts an AsyncImpl into Int64CounterObserver. func wrapInt64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64CounterObserver, error) { common, err := checkNewAsync(asyncInst, err) return Int64CounterObserver{asyncInstrument: common}, err } // wrapFloat64CounterObserverInstrument converts an AsyncImpl into Float64CounterObserver. func wrapFloat64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64CounterObserver, error) { common, err := checkNewAsync(asyncInst, err) return Float64CounterObserver{asyncInstrument: common}, err } // wrapInt64UpDownCounterObserverInstrument converts an AsyncImpl into Int64UpDownCounterObserver. func wrapInt64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64UpDownCounterObserver, error) { common, err := checkNewAsync(asyncInst, err) return Int64UpDownCounterObserver{asyncInstrument: common}, err } // wrapFloat64UpDownCounterObserverInstrument converts an AsyncImpl into Float64UpDownCounterObserver. func wrapFloat64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64UpDownCounterObserver, error) { common, err := checkNewAsync(asyncInst, err) return Float64UpDownCounterObserver{asyncInstrument: common}, err } // BatchObserver represents an Observer callback that can report // observations for multiple instruments. type BatchObserver struct { meter Meter runner sdkapi.AsyncBatchRunner } // Int64GaugeObserver is a metric that captures a set of int64 values at a // point in time. type Int64GaugeObserver struct { asyncInstrument } // Float64GaugeObserver is a metric that captures a set of float64 values // at a point in time. type Float64GaugeObserver struct { asyncInstrument } // Int64CounterObserver is a metric that captures a precomputed sum of // int64 values at a point in time. type Int64CounterObserver struct { asyncInstrument } // Float64CounterObserver is a metric that captures a precomputed sum of // float64 values at a point in time. type Float64CounterObserver struct { asyncInstrument } // Int64UpDownCounterObserver is a metric that captures a precomputed sum of // int64 values at a point in time. type Int64UpDownCounterObserver struct { asyncInstrument } // Float64UpDownCounterObserver is a metric that captures a precomputed sum of // float64 values at a point in time. type Float64UpDownCounterObserver struct { asyncInstrument } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (i Int64GaugeObserver) Observation(v int64) Observation { return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v)) } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (f Float64GaugeObserver) Observation(v float64) Observation { return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v)) } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (i Int64CounterObserver) Observation(v int64) Observation { return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v)) } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (f Float64CounterObserver) Observation(v float64) Observation { return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v)) } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (i Int64UpDownCounterObserver) Observation(v int64) Observation { return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v)) } // Observation returns an Observation, a BatchObserverFunc // argument, for an asynchronous integer instrument. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (f Float64UpDownCounterObserver) Observation(v float64) Observation { return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v)) } // syncInstrument contains a SyncImpl. type syncInstrument struct { instrument sdkapi.SyncImpl } // syncBoundInstrument contains a BoundSyncImpl. type syncBoundInstrument struct { boundInstrument sdkapi.BoundSyncImpl } // asyncInstrument contains a AsyncImpl. type asyncInstrument struct { instrument sdkapi.AsyncImpl } // AsyncImpl implements AsyncImpl. func (a asyncInstrument) AsyncImpl() sdkapi.AsyncImpl { return a.instrument } // SyncImpl returns the implementation object for synchronous instruments. func (s syncInstrument) SyncImpl() sdkapi.SyncImpl { return s.instrument } func (s syncInstrument) bind(labels []attribute.KeyValue) syncBoundInstrument { return newSyncBoundInstrument(s.instrument.Bind(labels)) } func (s syncInstrument) float64Measurement(value float64) Measurement { return sdkapi.NewMeasurement(s.instrument, number.NewFloat64Number(value)) } func (s syncInstrument) int64Measurement(value int64) Measurement { return sdkapi.NewMeasurement(s.instrument, number.NewInt64Number(value)) } func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []attribute.KeyValue) { s.instrument.RecordOne(ctx, number, labels) } func (h syncBoundInstrument) directRecord(ctx context.Context, number number.Number) { h.boundInstrument.RecordOne(ctx, number) } // Unbind calls SyncImpl.Unbind. func (h syncBoundInstrument) Unbind() { h.boundInstrument.Unbind() } // checkNewAsync receives an AsyncImpl and potential // error, and returns the same types, checking for and ensuring that // the returned interface is not nil. func checkNewAsync(instrument sdkapi.AsyncImpl, err error) (asyncInstrument, error) { if instrument == nil { if err == nil { err = ErrSDKReturnedNilImpl } instrument = sdkapi.NewNoopAsyncInstrument() } return asyncInstrument{ instrument: instrument, }, err } // checkNewSync receives an SyncImpl and potential // error, and returns the same types, checking for and ensuring that // the returned interface is not nil. func checkNewSync(instrument sdkapi.SyncImpl, err error) (syncInstrument, error) { if instrument == nil { if err == nil { err = ErrSDKReturnedNilImpl } // Note: an alternate behavior would be to synthesize a new name // or group all duplicately-named instruments of a certain type // together and use a tag for the original name, e.g., // name = 'invalid.counter.int64' // label = 'original-name=duplicate-counter-name' instrument = sdkapi.NewNoopSyncInstrument() } return syncInstrument{ instrument: instrument, }, err } func newSyncBoundInstrument(boundInstrument sdkapi.BoundSyncImpl) syncBoundInstrument { return syncBoundInstrument{ boundInstrument: boundInstrument, } } // wrapInt64CounterInstrument converts a SyncImpl into Int64Counter. func wrapInt64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Counter, error) { common, err := checkNewSync(syncInst, err) return Int64Counter{syncInstrument: common}, err } // wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter. func wrapFloat64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Counter, error) { common, err := checkNewSync(syncInst, err) return Float64Counter{syncInstrument: common}, err } // wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter. func wrapInt64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64UpDownCounter, error) { common, err := checkNewSync(syncInst, err) return Int64UpDownCounter{syncInstrument: common}, err } // wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter. func wrapFloat64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64UpDownCounter, error) { common, err := checkNewSync(syncInst, err) return Float64UpDownCounter{syncInstrument: common}, err } // wrapInt64HistogramInstrument converts a SyncImpl into Int64Histogram. func wrapInt64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Histogram, error) { common, err := checkNewSync(syncInst, err) return Int64Histogram{syncInstrument: common}, err } // wrapFloat64HistogramInstrument converts a SyncImpl into Float64Histogram. func wrapFloat64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Histogram, error) { common, err := checkNewSync(syncInst, err) return Float64Histogram{syncInstrument: common}, err } // Float64Counter is a metric that accumulates float64 values. type Float64Counter struct { syncInstrument } // Int64Counter is a metric that accumulates int64 values. type Int64Counter struct { syncInstrument } // BoundFloat64Counter is a bound instrument for Float64Counter. // // It inherits the Unbind function from syncBoundInstrument. type BoundFloat64Counter struct { syncBoundInstrument } // BoundInt64Counter is a boundInstrument for Int64Counter. // // It inherits the Unbind function from syncBoundInstrument. type BoundInt64Counter struct { syncBoundInstrument } // Bind creates a bound instrument for this counter. The labels are // associated with values recorded via subsequent calls to Record. func (c Float64Counter) Bind(labels ...attribute.KeyValue) (h BoundFloat64Counter) { h.syncBoundInstrument = c.bind(labels) return } // Bind creates a bound instrument for this counter. The labels are // associated with values recorded via subsequent calls to Record. func (c Int64Counter) Bind(labels ...attribute.KeyValue) (h BoundInt64Counter) { h.syncBoundInstrument = c.bind(labels) return } // Measurement creates a Measurement object to use with batch // recording. func (c Float64Counter) Measurement(value float64) Measurement { return c.float64Measurement(value) } // Measurement creates a Measurement object to use with batch // recording. func (c Int64Counter) Measurement(value int64) Measurement { return c.int64Measurement(value) } // Add adds the value to the counter's sum. The labels should contain // the keys and values to be associated with this value. func (c Float64Counter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewFloat64Number(value), labels) } // Add adds the value to the counter's sum. The labels should contain // the keys and values to be associated with this value. func (c Int64Counter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewInt64Number(value), labels) } // Add adds the value to the counter's sum using the labels // previously bound to this counter via Bind() func (b BoundFloat64Counter) Add(ctx context.Context, value float64) { b.directRecord(ctx, number.NewFloat64Number(value)) } // Add adds the value to the counter's sum using the labels // previously bound to this counter via Bind() func (b BoundInt64Counter) Add(ctx context.Context, value int64) { b.directRecord(ctx, number.NewInt64Number(value)) } // Float64UpDownCounter is a metric instrument that sums floating // point values. type Float64UpDownCounter struct { syncInstrument } // Int64UpDownCounter is a metric instrument that sums integer values. type Int64UpDownCounter struct { syncInstrument } // BoundFloat64UpDownCounter is a bound instrument for Float64UpDownCounter. // // It inherits the Unbind function from syncBoundInstrument. type BoundFloat64UpDownCounter struct { syncBoundInstrument } // BoundInt64UpDownCounter is a boundInstrument for Int64UpDownCounter. // // It inherits the Unbind function from syncBoundInstrument. type BoundInt64UpDownCounter struct { syncBoundInstrument } // Bind creates a bound instrument for this counter. The labels are // associated with values recorded via subsequent calls to Record. func (c Float64UpDownCounter) Bind(labels ...attribute.KeyValue) (h BoundFloat64UpDownCounter) { h.syncBoundInstrument = c.bind(labels) return } // Bind creates a bound instrument for this counter. The labels are // associated with values recorded via subsequent calls to Record. func (c Int64UpDownCounter) Bind(labels ...attribute.KeyValue) (h BoundInt64UpDownCounter) { h.syncBoundInstrument = c.bind(labels) return } // Measurement creates a Measurement object to use with batch // recording. func (c Float64UpDownCounter) Measurement(value float64) Measurement { return c.float64Measurement(value) } // Measurement creates a Measurement object to use with batch // recording. func (c Int64UpDownCounter) Measurement(value int64) Measurement { return c.int64Measurement(value) } // Add adds the value to the counter's sum. The labels should contain // the keys and values to be associated with this value. func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewFloat64Number(value), labels) } // Add adds the value to the counter's sum. The labels should contain // the keys and values to be associated with this value. func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewInt64Number(value), labels) } // Add adds the value to the counter's sum using the labels // previously bound to this counter via Bind() func (b BoundFloat64UpDownCounter) Add(ctx context.Context, value float64) { b.directRecord(ctx, number.NewFloat64Number(value)) } // Add adds the value to the counter's sum using the labels // previously bound to this counter via Bind() func (b BoundInt64UpDownCounter) Add(ctx context.Context, value int64) { b.directRecord(ctx, number.NewInt64Number(value)) } // Float64Histogram is a metric that records float64 values. type Float64Histogram struct { syncInstrument } // Int64Histogram is a metric that records int64 values. type Int64Histogram struct { syncInstrument } // BoundFloat64Histogram is a bound instrument for Float64Histogram. // // It inherits the Unbind function from syncBoundInstrument. type BoundFloat64Histogram struct { syncBoundInstrument } // BoundInt64Histogram is a bound instrument for Int64Histogram. // // It inherits the Unbind function from syncBoundInstrument. type BoundInt64Histogram struct { syncBoundInstrument } // Bind creates a bound instrument for this Histogram. The labels are // associated with values recorded via subsequent calls to Record. func (c Float64Histogram) Bind(labels ...attribute.KeyValue) (h BoundFloat64Histogram) { h.syncBoundInstrument = c.bind(labels) return } // Bind creates a bound instrument for this Histogram. The labels are // associated with values recorded via subsequent calls to Record. func (c Int64Histogram) Bind(labels ...attribute.KeyValue) (h BoundInt64Histogram) { h.syncBoundInstrument = c.bind(labels) return } // Measurement creates a Measurement object to use with batch // recording. func (c Float64Histogram) Measurement(value float64) Measurement { return c.float64Measurement(value) } // Measurement creates a Measurement object to use with batch // recording. func (c Int64Histogram) Measurement(value int64) Measurement { return c.int64Measurement(value) } // Record adds a new value to the list of Histogram's records. The // labels should contain the keys and values to be associated with // this value. func (c Float64Histogram) Record(ctx context.Context, value float64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewFloat64Number(value), labels) } // Record adds a new value to the Histogram's distribution. The // labels should contain the keys and values to be associated with // this value. func (c Int64Histogram) Record(ctx context.Context, value int64, labels ...attribute.KeyValue) { c.directRecord(ctx, number.NewInt64Number(value), labels) } // Record adds a new value to the Histogram's distribution using the labels // previously bound to the Histogram via Bind(). func (b BoundFloat64Histogram) Record(ctx context.Context, value float64) { b.directRecord(ctx, number.NewFloat64Number(value)) } // Record adds a new value to the Histogram's distribution using the labels // previously bound to the Histogram via Bind(). func (b BoundInt64Histogram) Record(ctx context.Context, value int64) { b.directRecord(ctx, number.NewInt64Number(value)) } golang-opentelemetry-otel-1.1.0/metric/metric_test.go000066400000000000000000000371121414226744000227570ustar00rootroot00000000000000// 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 metric_test import ( "context" "errors" "testing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/metric/unit" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var Must = metric.Must var ( syncKinds = []sdkapi.InstrumentKind{ sdkapi.HistogramInstrumentKind, sdkapi.CounterInstrumentKind, sdkapi.UpDownCounterInstrumentKind, } asyncKinds = []sdkapi.InstrumentKind{ sdkapi.GaugeObserverInstrumentKind, sdkapi.CounterObserverInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind, } addingKinds = []sdkapi.InstrumentKind{ sdkapi.CounterInstrumentKind, sdkapi.UpDownCounterInstrumentKind, sdkapi.CounterObserverInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind, } groupingKinds = []sdkapi.InstrumentKind{ sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, } monotonicKinds = []sdkapi.InstrumentKind{ sdkapi.CounterInstrumentKind, sdkapi.CounterObserverInstrumentKind, } nonMonotonicKinds = []sdkapi.InstrumentKind{ sdkapi.UpDownCounterInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind, sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, } precomputedSumKinds = []sdkapi.InstrumentKind{ sdkapi.CounterObserverInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind, } nonPrecomputedSumKinds = []sdkapi.InstrumentKind{ sdkapi.CounterInstrumentKind, sdkapi.UpDownCounterInstrumentKind, sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, } ) func TestSynchronous(t *testing.T) { for _, k := range syncKinds { require.True(t, k.Synchronous()) require.False(t, k.Asynchronous()) } for _, k := range asyncKinds { require.True(t, k.Asynchronous()) require.False(t, k.Synchronous()) } } func TestGrouping(t *testing.T) { for _, k := range groupingKinds { require.True(t, k.Grouping()) require.False(t, k.Adding()) } for _, k := range addingKinds { require.True(t, k.Adding()) require.False(t, k.Grouping()) } } func TestMonotonic(t *testing.T) { for _, k := range monotonicKinds { require.True(t, k.Monotonic()) } for _, k := range nonMonotonicKinds { require.False(t, k.Monotonic()) } } func TestPrecomputedSum(t *testing.T) { for _, k := range precomputedSumKinds { require.True(t, k.PrecomputedSum()) } for _, k := range nonPrecomputedSumKinds { require.False(t, k.PrecomputedSum()) } } func checkSyncBatches(ctx context.Context, t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, instrument sdkapi.InstrumentImpl, expected ...float64) { t.Helper() batchesCount := len(provider.MeasurementBatches) if len(provider.MeasurementBatches) != len(expected) { t.Errorf("Expected %d recorded measurement batches, got %d", batchesCount, len(provider.MeasurementBatches)) } recorded := metrictest.AsStructs(provider.MeasurementBatches) for i, batch := range provider.MeasurementBatches { if len(batch.Measurements) != 1 { t.Errorf("Expected 1 measurement in batch %d, got %d", i, len(batch.Measurements)) } measurement := batch.Measurements[0] descriptor := measurement.Instrument.Descriptor() expected := metrictest.Measured{ Name: descriptor.Name(), Library: metrictest.Library{ InstrumentationName: "apitest", }, Labels: metrictest.LabelsToMap(labels...), Number: metrictest.ResolveNumberByKind(t, nkind, expected[i]), } require.Equal(t, expected, recorded[i]) } } func TestOptions(t *testing.T) { type testcase struct { name string opts []metric.InstrumentOption desc string unit unit.Unit } testcases := []testcase{ { name: "no opts", opts: nil, desc: "", unit: "", }, { name: "description", opts: []metric.InstrumentOption{ metric.WithDescription("stuff"), }, desc: "stuff", unit: "", }, { name: "description override", opts: []metric.InstrumentOption{ metric.WithDescription("stuff"), metric.WithDescription("things"), }, desc: "things", unit: "", }, { name: "unit", opts: []metric.InstrumentOption{ metric.WithUnit("s"), }, desc: "", unit: "s", }, { name: "description override", opts: []metric.InstrumentOption{ metric.WithDescription("stuff"), metric.WithDescription("things"), }, desc: "things", unit: "", }, { name: "unit", opts: []metric.InstrumentOption{ metric.WithUnit("s"), }, desc: "", unit: "s", }, { name: "unit override", opts: []metric.InstrumentOption{ metric.WithUnit("s"), metric.WithUnit("h"), }, desc: "", unit: "h", }, { name: "all", opts: []metric.InstrumentOption{ metric.WithDescription("stuff"), metric.WithUnit("s"), }, desc: "stuff", unit: "s", }, } for idx, tt := range testcases { t.Logf("Testing counter case %s (%d)", tt.name, idx) cfg := metric.NewInstrumentConfig(tt.opts...) if diff := cmp.Diff(cfg.Description(), tt.desc); diff != "" { t.Errorf("Compare Description: -got +want %s", diff) } if diff := cmp.Diff(cfg.Unit(), tt.unit); diff != "" { t.Errorf("Compare Unit: -got +want %s", diff) } } } func testPair() (*metrictest.MeterProvider, metric.Meter) { provider := metrictest.NewMeterProvider() return provider, provider.Meter("apitest") } func TestCounter(t *testing.T) { // N.B. the API does not check for negative // values, that's the SDK's responsibility. t.Run("float64 counter", func(t *testing.T) { provider, meter := testPair() c := Must(meter).NewFloat64Counter("test.counter.float") ctx := context.Background() labels := []attribute.KeyValue{attribute.String("A", "B")} c.Add(ctx, 1994.1, labels...) boundInstrument := c.Bind(labels...) boundInstrument.Add(ctx, -742) meter.RecordBatch(ctx, labels, c.Measurement(42)) checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(), 1994.1, -742, 42, ) }) t.Run("int64 counter", func(t *testing.T) { provider, meter := testPair() c := Must(meter).NewInt64Counter("test.counter.int") ctx := context.Background() labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")} c.Add(ctx, 42, labels...) boundInstrument := c.Bind(labels...) boundInstrument.Add(ctx, 4200) meter.RecordBatch(ctx, labels, c.Measurement(420000)) checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(), 42, 4200, 420000, ) }) t.Run("int64 updowncounter", func(t *testing.T) { provider, meter := testPair() c := Must(meter).NewInt64UpDownCounter("test.updowncounter.int") ctx := context.Background() labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")} c.Add(ctx, 100, labels...) boundInstrument := c.Bind(labels...) boundInstrument.Add(ctx, -100) meter.RecordBatch(ctx, labels, c.Measurement(42)) checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(), 100, -100, 42, ) }) t.Run("float64 updowncounter", func(t *testing.T) { provider, meter := testPair() c := Must(meter).NewFloat64UpDownCounter("test.updowncounter.float") ctx := context.Background() labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")} c.Add(ctx, 100.1, labels...) boundInstrument := c.Bind(labels...) boundInstrument.Add(ctx, -76) meter.RecordBatch(ctx, labels, c.Measurement(-100.1)) checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(), 100.1, -76, -100.1, ) }) } func TestHistogram(t *testing.T) { t.Run("float64 histogram", func(t *testing.T) { provider, meter := testPair() m := Must(meter).NewFloat64Histogram("test.histogram.float") ctx := context.Background() labels := []attribute.KeyValue{} m.Record(ctx, 42, labels...) boundInstrument := m.Bind(labels...) boundInstrument.Record(ctx, 0) meter.RecordBatch(ctx, labels, m.Measurement(-100.5)) checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(), 42, 0, -100.5, ) }) t.Run("int64 histogram", func(t *testing.T) { provider, meter := testPair() m := Must(meter).NewInt64Histogram("test.histogram.int") ctx := context.Background() labels := []attribute.KeyValue{attribute.Int("I", 1)} m.Record(ctx, 173, labels...) boundInstrument := m.Bind(labels...) boundInstrument.Record(ctx, 80) meter.RecordBatch(ctx, labels, m.Measurement(0)) checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(), 173, 80, 0, ) }) } func TestObserverInstruments(t *testing.T) { t.Run("float gauge", func(t *testing.T) { labels := []attribute.KeyValue{attribute.String("O", "P")} provider, meter := testPair() o := Must(meter).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(), 42.1, ) }) t.Run("int gauge", func(t *testing.T) { labels := []attribute.KeyValue{} provider, meter := testPair() o := Must(meter).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(), -142, ) }) t.Run("float counterobserver", func(t *testing.T) { labels := []attribute.KeyValue{attribute.String("O", "P")} provider, meter := testPair() o := Must(meter).NewFloat64CounterObserver("test.counter.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(), 42.1, ) }) t.Run("int counterobserver", func(t *testing.T) { labels := []attribute.KeyValue{} provider, meter := testPair() o := Must(meter).NewInt64CounterObserver("test.counter.int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(), -142, ) }) t.Run("float updowncounterobserver", func(t *testing.T) { labels := []attribute.KeyValue{attribute.String("O", "P")} provider, meter := testPair() o := Must(meter).NewFloat64UpDownCounterObserver("test.updowncounter.float", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(42.1, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(), 42.1, ) }) t.Run("int updowncounterobserver", func(t *testing.T) { labels := []attribute.KeyValue{} provider, meter := testPair() o := Must(meter).NewInt64UpDownCounterObserver("test..int", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-142, labels...) }) provider.RunAsyncInstruments() checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(), -142, ) }) } func TestBatchObserverInstruments(t *testing.T) { provider, meter := testPair() var obs1 metric.Int64GaugeObserver var obs2 metric.Float64GaugeObserver labels := []attribute.KeyValue{ attribute.String("A", "B"), attribute.String("C", "D"), } cb := Must(meter).NewBatchObserver( func(_ context.Context, result metric.BatchObserverResult) { result.Observe(labels, obs1.Observation(42), obs2.Observation(42.0), ) }, ) obs1 = cb.NewInt64GaugeObserver("test.gauge.int") obs2 = cb.NewFloat64GaugeObserver("test.gauge.float") provider.RunAsyncInstruments() require.Len(t, provider.MeasurementBatches, 1) impl1 := obs1.AsyncImpl().Implementation().(*metrictest.Async) impl2 := obs2.AsyncImpl().Implementation().(*metrictest.Async) require.NotNil(t, impl1) require.NotNil(t, impl2) got := provider.MeasurementBatches[0] require.Equal(t, labels, got.Labels) require.Len(t, got.Measurements, 2) m1 := got.Measurements[0] require.Equal(t, impl1, m1.Instrument.Implementation().(*metrictest.Async)) require.Equal(t, 0, m1.Number.CompareNumber(number.Int64Kind, metrictest.ResolveNumberByKind(t, number.Int64Kind, 42))) m2 := got.Measurements[1] require.Equal(t, impl2, m2.Instrument.Implementation().(*metrictest.Async)) require.Equal(t, 0, m2.Number.CompareNumber(number.Float64Kind, metrictest.ResolveNumberByKind(t, number.Float64Kind, 42))) } func checkObserverBatch(t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, observer sdkapi.AsyncImpl, expected float64) { t.Helper() assert.Len(t, provider.MeasurementBatches, 1) if len(provider.MeasurementBatches) < 1 { return } o := observer.Implementation().(*metrictest.Async) if !assert.NotNil(t, o) { return } got := provider.MeasurementBatches[0] assert.Equal(t, labels, got.Labels) assert.Len(t, got.Measurements, 1) if len(got.Measurements) < 1 { return } measurement := got.Measurements[0] require.Equal(t, mkind, measurement.Instrument.Descriptor().InstrumentKind()) assert.Equal(t, o, measurement.Instrument.Implementation().(*metrictest.Async)) ft := metrictest.ResolveNumberByKind(t, nkind, expected) assert.Equal(t, 0, measurement.Number.CompareNumber(nkind, ft)) } type testWrappedMeter struct { } var _ sdkapi.MeterImpl = testWrappedMeter{} func (testWrappedMeter) RecordBatch(context.Context, []attribute.KeyValue, ...sdkapi.Measurement) { } func (testWrappedMeter) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) { return nil, nil } func (testWrappedMeter) NewAsyncInstrument(_ sdkapi.Descriptor, _ sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) { return nil, errors.New("Test wrap error") } func TestWrappedInstrumentError(t *testing.T) { impl := &testWrappedMeter{} meter := metric.WrapMeterImpl(impl) histogram, err := meter.NewInt64Histogram("test.histogram") require.Equal(t, err, metric.ErrSDKReturnedNilImpl) require.NotNil(t, histogram.SyncImpl()) observer, err := meter.NewInt64GaugeObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {}) require.NotNil(t, err) require.NotNil(t, observer.AsyncImpl()) } func TestNilCallbackObserverNoop(t *testing.T) { // Tests that a nil callback yields a no-op observer without error. _, meter := testPair() observer := Must(meter).NewInt64GaugeObserver("test.observer", nil) impl := observer.AsyncImpl().Implementation() desc := observer.AsyncImpl().Descriptor() require.Equal(t, nil, impl) require.Equal(t, "", desc.Name()) } golang-opentelemetry-otel-1.1.0/metric/metrictest/000077500000000000000000000000001414226744000222655ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/metrictest/alignment_test.go000066400000000000000000000021311414226744000256260ustar00rootroot00000000000000// 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 metrictest import ( "os" "testing" "unsafe" "go.opentelemetry.io/otel/internal/internaltest" ) // TestMain ensures struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []internaltest.FieldOffset{ { Name: "Batch.Measurments", Offset: unsafe.Offsetof(Batch{}.Measurements), }, { Name: "Measurement.Number", Offset: unsafe.Offsetof(Measurement{}.Number), }, } if !internaltest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } golang-opentelemetry-otel-1.1.0/metric/metrictest/meter.go000066400000000000000000000202161414226744000237310ustar00rootroot00000000000000// 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 metrictest // import "go.opentelemetry.io/otel/metric/metrictest" import ( "context" "sync" "testing" "go.opentelemetry.io/otel/attribute" internalmetric "go.opentelemetry.io/otel/internal/metric" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) type ( Handle struct { Instrument *Sync Labels []attribute.KeyValue } // Library is the same as "sdk/instrumentation".Library but there is // a package cycle to use it. Library struct { InstrumentationName string InstrumentationVersion string SchemaURL string } Batch struct { // Measurement needs to be aligned for 64-bit atomic operations. Measurements []Measurement Ctx context.Context Labels []attribute.KeyValue Library Library } // MeterImpl is an OpenTelemetry Meter implementation used for testing. MeterImpl struct { library Library provider *MeterProvider asyncInstruments *internalmetric.AsyncInstrumentState } // MeterProvider is a collection of named MeterImpls used for testing. MeterProvider struct { lock sync.Mutex MeasurementBatches []Batch impls []*MeterImpl } Measurement struct { // Number needs to be aligned for 64-bit atomic operations. Number number.Number Instrument sdkapi.InstrumentImpl } Instrument struct { meter *MeterImpl descriptor sdkapi.Descriptor } Async struct { Instrument runner sdkapi.AsyncRunner } Sync struct { Instrument } ) var ( _ sdkapi.SyncImpl = &Sync{} _ sdkapi.BoundSyncImpl = &Handle{} _ sdkapi.MeterImpl = &MeterImpl{} _ sdkapi.AsyncImpl = &Async{} ) // NewDescriptor is a test helper for constructing test metric // descriptors using standard options. func NewDescriptor(name string, ikind sdkapi.InstrumentKind, nkind number.Kind, opts ...metric.InstrumentOption) sdkapi.Descriptor { cfg := metric.NewInstrumentConfig(opts...) return sdkapi.NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit()) } func (i Instrument) Descriptor() sdkapi.Descriptor { return i.descriptor } func (a *Async) Implementation() interface{} { return a } func (s *Sync) Implementation() interface{} { return s } func (s *Sync) Bind(labels []attribute.KeyValue) sdkapi.BoundSyncImpl { return &Handle{ Instrument: s, Labels: labels, } } func (s *Sync) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) { s.meter.doRecordSingle(ctx, labels, s, number) } func (h *Handle) RecordOne(ctx context.Context, number number.Number) { h.Instrument.meter.doRecordSingle(ctx, h.Labels, h.Instrument, number) } func (h *Handle) Unbind() { } func (m *MeterImpl) doRecordSingle(ctx context.Context, labels []attribute.KeyValue, instrument sdkapi.InstrumentImpl, number number.Number) { m.collect(ctx, labels, []Measurement{{ Instrument: instrument, Number: number, }}) } // NewMeterProvider returns a MeterProvider suitable for testing. // When the test is complete, consult MeterProvider.MeasurementBatches. func NewMeterProvider() *MeterProvider { return &MeterProvider{} } // Meter implements metric.MeterProvider. func (p *MeterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter { p.lock.Lock() defer p.lock.Unlock() cfg := metric.NewMeterConfig(opts...) impl := &MeterImpl{ library: Library{ InstrumentationName: name, InstrumentationVersion: cfg.InstrumentationVersion(), SchemaURL: cfg.SchemaURL(), }, provider: p, asyncInstruments: internalmetric.NewAsyncInstrumentState(), } p.impls = append(p.impls, impl) return metric.WrapMeterImpl(impl) } // NewSyncInstrument implements sdkapi.MeterImpl. func (m *MeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) { return &Sync{ Instrument{ descriptor: descriptor, meter: m, }, }, nil } // NewAsyncInstrument implements sdkapi.MeterImpl. func (m *MeterImpl) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) { a := &Async{ Instrument: Instrument{ descriptor: descriptor, meter: m, }, runner: runner, } m.provider.registerAsyncInstrument(a, m, runner) return a, nil } // RecordBatch implements sdkapi.MeterImpl. func (m *MeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) { mm := make([]Measurement, len(measurements)) for i := 0; i < len(measurements); i++ { m := measurements[i] mm[i] = Measurement{ Instrument: m.SyncImpl().Implementation().(*Sync), Number: m.Number(), } } m.collect(ctx, labels, mm) } // CollectAsync is called from asyncInstruments.Run() with the lock held. func (m *MeterImpl) CollectAsync(labels []attribute.KeyValue, obs ...sdkapi.Observation) { mm := make([]Measurement, len(obs)) for i := 0; i < len(obs); i++ { o := obs[i] mm[i] = Measurement{ Instrument: o.AsyncImpl(), Number: o.Number(), } } m.collect(context.Background(), labels, mm) } // collect is called from CollectAsync() or RecordBatch() with the lock held. func (m *MeterImpl) collect(ctx context.Context, labels []attribute.KeyValue, measurements []Measurement) { m.provider.addMeasurement(Batch{ Ctx: ctx, Labels: labels, Measurements: measurements, Library: m.library, }) } // registerAsyncInstrument locks the provider and registers the new Async instrument. func (p *MeterProvider) registerAsyncInstrument(a *Async, m *MeterImpl, runner sdkapi.AsyncRunner) { p.lock.Lock() defer p.lock.Unlock() m.asyncInstruments.Register(a, runner) } // addMeasurement locks the provider and adds the new measurement batch. func (p *MeterProvider) addMeasurement(b Batch) { p.lock.Lock() defer p.lock.Unlock() p.MeasurementBatches = append(p.MeasurementBatches, b) } // copyImpls locks the provider and copies the current list of *MeterImpls. func (p *MeterProvider) copyImpls() []*MeterImpl { p.lock.Lock() defer p.lock.Unlock() cpy := make([]*MeterImpl, len(p.impls)) copy(cpy, p.impls) return cpy } // RunAsyncInstruments is used in tests to trigger collection from // asynchronous instruments. func (p *MeterProvider) RunAsyncInstruments() { for _, impl := range p.copyImpls() { impl.asyncInstruments.Run(context.Background(), impl) } } // Measured is the helper struct which provides flat representation of recorded measurements // to simplify testing type Measured struct { Name string Labels map[attribute.Key]attribute.Value Number number.Number Library Library } // LabelsToMap converts label set to keyValue map, to be easily used in tests func LabelsToMap(kvs ...attribute.KeyValue) map[attribute.Key]attribute.Value { m := map[attribute.Key]attribute.Value{} for _, label := range kvs { m[label.Key] = label.Value } return m } // AsStructs converts recorded batches to array of flat, readable Measured helper structures func AsStructs(batches []Batch) []Measured { var r []Measured for _, batch := range batches { for _, m := range batch.Measurements { r = append(r, Measured{ Name: m.Instrument.Descriptor().Name(), Labels: LabelsToMap(batch.Labels...), Number: m.Number, Library: batch.Library, }) } } return r } // ResolveNumberByKind takes defined metric descriptor creates a concrete typed metric number func ResolveNumberByKind(t *testing.T, kind number.Kind, value float64) number.Number { t.Helper() switch kind { case number.Int64Kind: return number.NewInt64Number(int64(value)) case number.Float64Kind: return number.NewFloat64Number(value) } panic("invalid number kind") } golang-opentelemetry-otel-1.1.0/metric/noop.go000066400000000000000000000021261414226744000214050ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/metric" type noopMeterProvider struct{} // NewNoopMeterProvider returns an implementation of MeterProvider that // performs no operations. The Meter and Instrument created from the returned // MeterProvider also perform no operations. func NewNoopMeterProvider() MeterProvider { return noopMeterProvider{} } var _ MeterProvider = noopMeterProvider{} func (noopMeterProvider) Meter(instrumentationName string, opts ...MeterOption) Meter { return Meter{} } golang-opentelemetry-otel-1.1.0/metric/noop_test.go000066400000000000000000000020271414226744000224440ustar00rootroot00000000000000// 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 metric import ( "testing" ) func TestNewNoopMeterProvider(t *testing.T) { got, want := NewNoopMeterProvider(), noopMeterProvider{} if got != want { t.Errorf("NewNoopMeterProvider() returned %#v, want %#v", got, want) } } func TestNoopMeterProviderMeter(t *testing.T) { mp := NewNoopMeterProvider() got, want := mp.Meter(""), Meter{} if got != want { t.Errorf("noopMeterProvider.Meter() returned %#v, want %#v", got, want) } } golang-opentelemetry-otel-1.1.0/metric/number/000077500000000000000000000000001414226744000213725ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/number/doc.go000066400000000000000000000017421414226744000224720ustar00rootroot00000000000000// 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 number provides a number abstraction for instruments that either support int64 or float64 input values. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. */ package number // import "go.opentelemetry.io/otel/metric/number" golang-opentelemetry-otel-1.1.0/metric/number/kind_string.go000066400000000000000000000011161414226744000242330ustar00rootroot00000000000000// Code generated by "stringer -type=Kind"; DO NOT EDIT. package number import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Int64Kind-0] _ = x[Float64Kind-1] } const _Kind_name = "Int64KindFloat64Kind" var _Kind_index = [...]uint8{0, 9, 20} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" } return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] } golang-opentelemetry-otel-1.1.0/metric/number/number.go000066400000000000000000000346131414226744000232200ustar00rootroot00000000000000// 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 number // import "go.opentelemetry.io/otel/metric/number" //go:generate stringer -type=Kind import ( "fmt" "math" "sync/atomic" "go.opentelemetry.io/otel/internal" ) // Kind describes the data type of the Number. type Kind int8 const ( // Int64Kind means that the Number stores int64. Int64Kind Kind = iota // Float64Kind means that the Number stores float64. Float64Kind ) // Zero returns a zero value for a given Kind func (k Kind) Zero() Number { switch k { case Int64Kind: return NewInt64Number(0) case Float64Kind: return NewFloat64Number(0.) default: return Number(0) } } // Minimum returns the minimum representable value // for a given Kind func (k Kind) Minimum() Number { switch k { case Int64Kind: return NewInt64Number(math.MinInt64) case Float64Kind: return NewFloat64Number(-1. * math.MaxFloat64) default: return Number(0) } } // Maximum returns the maximum representable value // for a given Kind func (k Kind) Maximum() Number { switch k { case Int64Kind: return NewInt64Number(math.MaxInt64) case Float64Kind: return NewFloat64Number(math.MaxFloat64) default: return Number(0) } } // Number represents either an integral or a floating point value. It // needs to be accompanied with a source of Kind that describes // the actual type of the value stored within Number. type Number uint64 // - constructors // NewNumberFromRaw creates a new Number from a raw value. func NewNumberFromRaw(r uint64) Number { return Number(r) } // NewInt64Number creates an integral Number. func NewInt64Number(i int64) Number { return NewNumberFromRaw(internal.Int64ToRaw(i)) } // NewFloat64Number creates a floating point Number. func NewFloat64Number(f float64) Number { return NewNumberFromRaw(internal.Float64ToRaw(f)) } // NewNumberSignChange returns a number with the same magnitude and // the opposite sign. `kind` must describe the kind of number in `nn`. func NewNumberSignChange(kind Kind, nn Number) Number { switch kind { case Int64Kind: return NewInt64Number(-nn.AsInt64()) case Float64Kind: return NewFloat64Number(-nn.AsFloat64()) } return nn } // - as x // AsNumber gets the Number. func (n *Number) AsNumber() Number { return *n } // AsRaw gets the uninterpreted raw value. Might be useful for some // atomic operations. func (n *Number) AsRaw() uint64 { return uint64(*n) } // AsInt64 assumes that the value contains an int64 and returns it as // such. func (n *Number) AsInt64() int64 { return internal.RawToInt64(n.AsRaw()) } // AsFloat64 assumes that the measurement value contains a float64 and // returns it as such. func (n *Number) AsFloat64() float64 { return internal.RawToFloat64(n.AsRaw()) } // - as x atomic // AsNumberAtomic gets the Number atomically. func (n *Number) AsNumberAtomic() Number { return NewNumberFromRaw(n.AsRawAtomic()) } // AsRawAtomic gets the uninterpreted raw value atomically. Might be // useful for some atomic operations. func (n *Number) AsRawAtomic() uint64 { return atomic.LoadUint64(n.AsRawPtr()) } // AsInt64Atomic assumes that the number contains an int64 and returns // it as such atomically. func (n *Number) AsInt64Atomic() int64 { return atomic.LoadInt64(n.AsInt64Ptr()) } // AsFloat64Atomic assumes that the measurement value contains a // float64 and returns it as such atomically. func (n *Number) AsFloat64Atomic() float64 { return internal.RawToFloat64(n.AsRawAtomic()) } // - as x ptr // AsRawPtr gets the pointer to the raw, uninterpreted raw // value. Might be useful for some atomic operations. func (n *Number) AsRawPtr() *uint64 { return (*uint64)(n) } // AsInt64Ptr assumes that the number contains an int64 and returns a // pointer to it. func (n *Number) AsInt64Ptr() *int64 { return internal.RawPtrToInt64Ptr(n.AsRawPtr()) } // AsFloat64Ptr assumes that the number contains a float64 and returns a // pointer to it. func (n *Number) AsFloat64Ptr() *float64 { return internal.RawPtrToFloat64Ptr(n.AsRawPtr()) } // - coerce // CoerceToInt64 casts the number to int64. May result in // data/precision loss. func (n *Number) CoerceToInt64(kind Kind) int64 { switch kind { case Int64Kind: return n.AsInt64() case Float64Kind: return int64(n.AsFloat64()) default: // you get what you deserve return 0 } } // CoerceToFloat64 casts the number to float64. May result in // data/precision loss. func (n *Number) CoerceToFloat64(kind Kind) float64 { switch kind { case Int64Kind: return float64(n.AsInt64()) case Float64Kind: return n.AsFloat64() default: // you get what you deserve return 0 } } // - set // SetNumber sets the number to the passed number. Both should be of // the same kind. func (n *Number) SetNumber(nn Number) { *n.AsRawPtr() = nn.AsRaw() } // SetRaw sets the number to the passed raw value. Both number and the // raw number should represent the same kind. func (n *Number) SetRaw(r uint64) { *n.AsRawPtr() = r } // SetInt64 assumes that the number contains an int64 and sets it to // the passed value. func (n *Number) SetInt64(i int64) { *n.AsInt64Ptr() = i } // SetFloat64 assumes that the number contains a float64 and sets it // to the passed value. func (n *Number) SetFloat64(f float64) { *n.AsFloat64Ptr() = f } // - set atomic // SetNumberAtomic sets the number to the passed number // atomically. Both should be of the same kind. func (n *Number) SetNumberAtomic(nn Number) { atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw()) } // SetRawAtomic sets the number to the passed raw value // atomically. Both number and the raw number should represent the // same kind. func (n *Number) SetRawAtomic(r uint64) { atomic.StoreUint64(n.AsRawPtr(), r) } // SetInt64Atomic assumes that the number contains an int64 and sets // it to the passed value atomically. func (n *Number) SetInt64Atomic(i int64) { atomic.StoreInt64(n.AsInt64Ptr(), i) } // SetFloat64Atomic assumes that the number contains a float64 and // sets it to the passed value atomically. func (n *Number) SetFloat64Atomic(f float64) { atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f)) } // - swap // SwapNumber sets the number to the passed number and returns the old // number. Both this number and the passed number should be of the // same kind. func (n *Number) SwapNumber(nn Number) Number { old := *n n.SetNumber(nn) return old } // SwapRaw sets the number to the passed raw value and returns the old // raw value. Both number and the raw number should represent the same // kind. func (n *Number) SwapRaw(r uint64) uint64 { old := n.AsRaw() n.SetRaw(r) return old } // SwapInt64 assumes that the number contains an int64, sets it to the // passed value and returns the old int64 value. func (n *Number) SwapInt64(i int64) int64 { old := n.AsInt64() n.SetInt64(i) return old } // SwapFloat64 assumes that the number contains an float64, sets it to // the passed value and returns the old float64 value. func (n *Number) SwapFloat64(f float64) float64 { old := n.AsFloat64() n.SetFloat64(f) return old } // - swap atomic // SwapNumberAtomic sets the number to the passed number and returns // the old number atomically. Both this number and the passed number // should be of the same kind. func (n *Number) SwapNumberAtomic(nn Number) Number { return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw())) } // SwapRawAtomic sets the number to the passed raw value and returns // the old raw value atomically. Both number and the raw number should // represent the same kind. func (n *Number) SwapRawAtomic(r uint64) uint64 { return atomic.SwapUint64(n.AsRawPtr(), r) } // SwapInt64Atomic assumes that the number contains an int64, sets it // to the passed value and returns the old int64 value atomically. func (n *Number) SwapInt64Atomic(i int64) int64 { return atomic.SwapInt64(n.AsInt64Ptr(), i) } // SwapFloat64Atomic assumes that the number contains an float64, sets // it to the passed value and returns the old float64 value // atomically. func (n *Number) SwapFloat64Atomic(f float64) float64 { return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f))) } // - add // AddNumber assumes that this and the passed number are of the passed // kind and adds the passed number to this number. func (n *Number) AddNumber(kind Kind, nn Number) { switch kind { case Int64Kind: n.AddInt64(nn.AsInt64()) case Float64Kind: n.AddFloat64(nn.AsFloat64()) } } // AddRaw assumes that this number and the passed raw value are of the // passed kind and adds the passed raw value to this number. func (n *Number) AddRaw(kind Kind, r uint64) { n.AddNumber(kind, NewNumberFromRaw(r)) } // AddInt64 assumes that the number contains an int64 and adds the // passed int64 to it. func (n *Number) AddInt64(i int64) { *n.AsInt64Ptr() += i } // AddFloat64 assumes that the number contains a float64 and adds the // passed float64 to it. func (n *Number) AddFloat64(f float64) { *n.AsFloat64Ptr() += f } // - add atomic // AddNumberAtomic assumes that this and the passed number are of the // passed kind and adds the passed number to this number atomically. func (n *Number) AddNumberAtomic(kind Kind, nn Number) { switch kind { case Int64Kind: n.AddInt64Atomic(nn.AsInt64()) case Float64Kind: n.AddFloat64Atomic(nn.AsFloat64()) } } // AddRawAtomic assumes that this number and the passed raw value are // of the passed kind and adds the passed raw value to this number // atomically. func (n *Number) AddRawAtomic(kind Kind, r uint64) { n.AddNumberAtomic(kind, NewNumberFromRaw(r)) } // AddInt64Atomic assumes that the number contains an int64 and adds // the passed int64 to it atomically. func (n *Number) AddInt64Atomic(i int64) { atomic.AddInt64(n.AsInt64Ptr(), i) } // AddFloat64Atomic assumes that the number contains a float64 and // adds the passed float64 to it atomically. func (n *Number) AddFloat64Atomic(f float64) { for { o := n.AsFloat64Atomic() if n.CompareAndSwapFloat64(o, o+f) { break } } } // - compare and swap (atomic only) // CompareAndSwapNumber does the atomic CAS operation on this // number. This number and passed old and new numbers should be of the // same kind. func (n *Number) CompareAndSwapNumber(on, nn Number) bool { return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw()) } // CompareAndSwapRaw does the atomic CAS operation on this // number. This number and passed old and new raw values should be of // the same kind. func (n *Number) CompareAndSwapRaw(or, nr uint64) bool { return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr) } // CompareAndSwapInt64 assumes that this number contains an int64 and // does the atomic CAS operation on it. func (n *Number) CompareAndSwapInt64(oi, ni int64) bool { return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni) } // CompareAndSwapFloat64 assumes that this number contains a float64 and // does the atomic CAS operation on it. func (n *Number) CompareAndSwapFloat64(of, nf float64) bool { return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf)) } // - compare // CompareNumber compares two Numbers given their kind. Both numbers // should have the same kind. This returns: // 0 if the numbers are equal // -1 if the subject `n` is less than the argument `nn` // +1 if the subject `n` is greater than the argument `nn` func (n *Number) CompareNumber(kind Kind, nn Number) int { switch kind { case Int64Kind: return n.CompareInt64(nn.AsInt64()) case Float64Kind: return n.CompareFloat64(nn.AsFloat64()) default: // you get what you deserve return 0 } } // CompareRaw compares two numbers, where one is input as a raw // uint64, interpreting both values as a `kind` of number. func (n *Number) CompareRaw(kind Kind, r uint64) int { return n.CompareNumber(kind, NewNumberFromRaw(r)) } // CompareInt64 assumes that the Number contains an int64 and performs // a comparison between the value and the other value. It returns the // typical result of the compare function: -1 if the value is less // than the other, 0 if both are equal, 1 if the value is greater than // the other. func (n *Number) CompareInt64(i int64) int { this := n.AsInt64() if this < i { return -1 } else if this > i { return 1 } return 0 } // CompareFloat64 assumes that the Number contains a float64 and // performs a comparison between the value and the other value. It // returns the typical result of the compare function: -1 if the value // is less than the other, 0 if both are equal, 1 if the value is // greater than the other. // // Do not compare NaN values. func (n *Number) CompareFloat64(f float64) int { this := n.AsFloat64() if this < f { return -1 } else if this > f { return 1 } return 0 } // - relations to zero // IsPositive returns true if the actual value is greater than zero. func (n *Number) IsPositive(kind Kind) bool { return n.compareWithZero(kind) > 0 } // IsNegative returns true if the actual value is less than zero. func (n *Number) IsNegative(kind Kind) bool { return n.compareWithZero(kind) < 0 } // IsZero returns true if the actual value is equal to zero. func (n *Number) IsZero(kind Kind) bool { return n.compareWithZero(kind) == 0 } // - misc // Emit returns a string representation of the raw value of the // Number. A %d is used for integral values, %f for floating point // values. func (n *Number) Emit(kind Kind) string { switch kind { case Int64Kind: return fmt.Sprintf("%d", n.AsInt64()) case Float64Kind: return fmt.Sprintf("%f", n.AsFloat64()) default: return "" } } // AsInterface returns the number as an interface{}, typically used // for Kind-correct JSON conversion. func (n *Number) AsInterface(kind Kind) interface{} { switch kind { case Int64Kind: return n.AsInt64() case Float64Kind: return n.AsFloat64() default: return math.NaN() } } // - private stuff func (n *Number) compareWithZero(kind Kind) int { switch kind { case Int64Kind: return n.CompareInt64(0) case Float64Kind: return n.CompareFloat64(0.) default: // you get what you deserve return 0 } } golang-opentelemetry-otel-1.1.0/metric/number/number_test.go000066400000000000000000000131031414226744000242460ustar00rootroot00000000000000// 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 number import ( "math" "testing" "unsafe" "github.com/stretchr/testify/require" ) func TestNumber(t *testing.T) { iNeg := NewInt64Number(-42) iZero := NewInt64Number(0) iPos := NewInt64Number(42) i64Numbers := [3]Number{iNeg, iZero, iPos} for idx, i := range []int64{-42, 0, 42} { n := i64Numbers[idx] if got := n.AsInt64(); got != i { t.Errorf("Number %#v (%s) int64 check failed, expected %d, got %d", n, n.Emit(Int64Kind), i, got) } } for _, n := range i64Numbers { expected := unsafe.Pointer(&n) got := unsafe.Pointer(n.AsRawPtr()) if expected != got { t.Errorf("Getting raw pointer failed, got %v, expected %v", got, expected) } } fNeg := NewFloat64Number(-42.) fZero := NewFloat64Number(0.) fPos := NewFloat64Number(42.) f64Numbers := [3]Number{fNeg, fZero, fPos} for idx, f := range []float64{-42., 0., 42.} { n := f64Numbers[idx] if got := n.AsFloat64(); got != f { t.Errorf("Number %#v (%s) float64 check failed, expected %f, got %f", n, n.Emit(Int64Kind), f, got) } } for _, n := range f64Numbers { expected := unsafe.Pointer(&n) got := unsafe.Pointer(n.AsRawPtr()) if expected != got { t.Errorf("Getting raw pointer failed, got %v, expected %v", got, expected) } } cmpsForNeg := [3]int{0, -1, -1} cmpsForZero := [3]int{1, 0, -1} cmpsForPos := [3]int{1, 1, 0} type testcase struct { // n needs to be aligned for 64-bit atomic operations. n Number // nums needs to be aligned for 64-bit atomic operations. nums [3]Number kind Kind pos bool zero bool neg bool cmps [3]int } testcases := []testcase{ { n: iNeg, kind: Int64Kind, pos: false, zero: false, neg: true, nums: i64Numbers, cmps: cmpsForNeg, }, { n: iZero, kind: Int64Kind, pos: false, zero: true, neg: false, nums: i64Numbers, cmps: cmpsForZero, }, { n: iPos, kind: Int64Kind, pos: true, zero: false, neg: false, nums: i64Numbers, cmps: cmpsForPos, }, { n: fNeg, kind: Float64Kind, pos: false, zero: false, neg: true, nums: f64Numbers, cmps: cmpsForNeg, }, { n: fZero, kind: Float64Kind, pos: false, zero: true, neg: false, nums: f64Numbers, cmps: cmpsForZero, }, { n: fPos, kind: Float64Kind, pos: true, zero: false, neg: false, nums: f64Numbers, cmps: cmpsForPos, }, } for _, tt := range testcases { if got := tt.n.IsPositive(tt.kind); got != tt.pos { t.Errorf("Number %#v (%s) positive check failed, expected %v, got %v", tt.n, tt.n.Emit(tt.kind), tt.pos, got) } if got := tt.n.IsZero(tt.kind); got != tt.zero { t.Errorf("Number %#v (%s) zero check failed, expected %v, got %v", tt.n, tt.n.Emit(tt.kind), tt.pos, got) } if got := tt.n.IsNegative(tt.kind); got != tt.neg { t.Errorf("Number %#v (%s) negative check failed, expected %v, got %v", tt.n, tt.n.Emit(tt.kind), tt.pos, got) } for i := 0; i < 3; i++ { if got := tt.n.CompareRaw(tt.kind, tt.nums[i].AsRaw()); got != tt.cmps[i] { t.Errorf("Number %#v (%s) compare check with %#v (%s) failed, expected %d, got %d", tt.n, tt.n.Emit(tt.kind), tt.nums[i], tt.nums[i].Emit(tt.kind), tt.cmps[i], got) } } } } func TestNumberZero(t *testing.T) { zero := Number(0) zerof := NewFloat64Number(0) zeroi := NewInt64Number(0) if zero != zerof || zero != zeroi { t.Errorf("Invalid zero representations") } } func TestNumberAsInterface(t *testing.T) { i64 := NewInt64Number(10) f64 := NewFloat64Number(11.11) require.Equal(t, int64(10), (&i64).AsInterface(Int64Kind).(int64)) require.Equal(t, 11.11, (&f64).AsInterface(Float64Kind).(float64)) } func TestNumberSignChange(t *testing.T) { t.Run("Int64", func(t *testing.T) { posInt := NewInt64Number(10) negInt := NewInt64Number(-10) require.Equal(t, posInt, NewNumberSignChange(Int64Kind, negInt)) require.Equal(t, negInt, NewNumberSignChange(Int64Kind, posInt)) }) t.Run("Float64", func(t *testing.T) { posFloat := NewFloat64Number(10) negFloat := NewFloat64Number(-10) require.Equal(t, posFloat, NewNumberSignChange(Float64Kind, negFloat)) require.Equal(t, negFloat, NewNumberSignChange(Float64Kind, posFloat)) }) t.Run("Float64Zero", func(t *testing.T) { posFloat := NewFloat64Number(0) negFloat := NewFloat64Number(math.Copysign(0, -1)) require.Equal(t, posFloat, NewNumberSignChange(Float64Kind, negFloat)) require.Equal(t, negFloat, NewNumberSignChange(Float64Kind, posFloat)) }) t.Run("Float64Inf", func(t *testing.T) { posFloat := NewFloat64Number(math.Inf(+1)) negFloat := NewFloat64Number(math.Inf(-1)) require.Equal(t, posFloat, NewNumberSignChange(Float64Kind, negFloat)) require.Equal(t, negFloat, NewNumberSignChange(Float64Kind, posFloat)) }) t.Run("Float64NaN", func(t *testing.T) { posFloat := NewFloat64Number(math.NaN()) negFloat := NewFloat64Number(math.Copysign(math.NaN(), -1)) require.Equal(t, posFloat, NewNumberSignChange(Float64Kind, negFloat)) require.Equal(t, negFloat, NewNumberSignChange(Float64Kind, posFloat)) }) } golang-opentelemetry-otel-1.1.0/metric/sdkapi/000077500000000000000000000000001414226744000213555ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/sdkapi/descriptor.go000066400000000000000000000041171414226744000240650ustar00rootroot00000000000000// 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 sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" import ( "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/unit" ) // Descriptor contains all the settings that describe an instrument, // including its name, metric kind, number kind, and the configurable // options. type Descriptor struct { name string instrumentKind InstrumentKind numberKind number.Kind description string unit unit.Unit } // NewDescriptor returns a Descriptor with the given contents. func NewDescriptor(name string, ikind InstrumentKind, nkind number.Kind, description string, unit unit.Unit) Descriptor { return Descriptor{ name: name, instrumentKind: ikind, numberKind: nkind, description: description, unit: unit, } } // Name returns the metric instrument's name. func (d Descriptor) Name() string { return d.name } // InstrumentKind returns the specific kind of instrument. func (d Descriptor) InstrumentKind() InstrumentKind { return d.instrumentKind } // Description provides a human-readable description of the metric // instrument. func (d Descriptor) Description() string { return d.description } // Unit describes the units of the metric instrument. Unitless // metrics return the empty string. func (d Descriptor) Unit() unit.Unit { return d.unit } // NumberKind returns whether this instrument is declared over int64, // float64, or uint64 values. func (d Descriptor) NumberKind() number.Kind { return d.numberKind } golang-opentelemetry-otel-1.1.0/metric/sdkapi/descriptor_test.go000066400000000000000000000022071414226744000251220ustar00rootroot00000000000000// 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 sdkapi import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/unit" ) func TestDescriptorGetters(t *testing.T) { d := NewDescriptor("name", HistogramInstrumentKind, number.Int64Kind, "my description", "my unit") require.Equal(t, "name", d.Name()) require.Equal(t, HistogramInstrumentKind, d.InstrumentKind()) require.Equal(t, number.Int64Kind, d.NumberKind()) require.Equal(t, "my description", d.Description()) require.Equal(t, unit.Unit("my unit"), d.Unit()) } golang-opentelemetry-otel-1.1.0/metric/sdkapi/instrumentkind.go000066400000000000000000000052001414226744000247570ustar00rootroot00000000000000// 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. //go:generate stringer -type=InstrumentKind package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" // InstrumentKind describes the kind of instrument. type InstrumentKind int8 const ( // HistogramInstrumentKind indicates a Histogram instrument. HistogramInstrumentKind InstrumentKind = iota // GaugeObserverInstrumentKind indicates an GaugeObserver instrument. GaugeObserverInstrumentKind // CounterInstrumentKind indicates a Counter instrument. CounterInstrumentKind // UpDownCounterInstrumentKind indicates a UpDownCounter instrument. UpDownCounterInstrumentKind // CounterObserverInstrumentKind indicates a CounterObserver instrument. CounterObserverInstrumentKind // UpDownCounterObserverInstrumentKind indicates a UpDownCounterObserver // instrument. UpDownCounterObserverInstrumentKind ) // Synchronous returns whether this is a synchronous kind of instrument. func (k InstrumentKind) Synchronous() bool { switch k { case CounterInstrumentKind, UpDownCounterInstrumentKind, HistogramInstrumentKind: return true } return false } // Asynchronous returns whether this is an asynchronous kind of instrument. func (k InstrumentKind) Asynchronous() bool { return !k.Synchronous() } // Adding returns whether this kind of instrument adds its inputs (as opposed to Grouping). func (k InstrumentKind) Adding() bool { switch k { case CounterInstrumentKind, UpDownCounterInstrumentKind, CounterObserverInstrumentKind, UpDownCounterObserverInstrumentKind: return true } return false } // Grouping returns whether this kind of instrument groups its inputs (as opposed to Adding). func (k InstrumentKind) Grouping() bool { return !k.Adding() } // Monotonic returns whether this kind of instrument exposes a non-decreasing sum. func (k InstrumentKind) Monotonic() bool { switch k { case CounterInstrumentKind, CounterObserverInstrumentKind: return true } return false } // PrecomputedSum returns whether this kind of instrument receives precomputed sums. func (k InstrumentKind) PrecomputedSum() bool { return k.Adding() && k.Asynchronous() } golang-opentelemetry-otel-1.1.0/metric/sdkapi/instrumentkind_string.go000066400000000000000000000020151414226744000263460ustar00rootroot00000000000000// Code generated by "stringer -type=InstrumentKind"; DO NOT EDIT. package sdkapi import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[HistogramInstrumentKind-0] _ = x[GaugeObserverInstrumentKind-1] _ = x[CounterInstrumentKind-2] _ = x[UpDownCounterInstrumentKind-3] _ = x[CounterObserverInstrumentKind-4] _ = x[UpDownCounterObserverInstrumentKind-5] } const _InstrumentKind_name = "HistogramInstrumentKindGaugeObserverInstrumentKindCounterInstrumentKindUpDownCounterInstrumentKindCounterObserverInstrumentKindUpDownCounterObserverInstrumentKind" var _InstrumentKind_index = [...]uint8{0, 23, 50, 71, 98, 127, 162} func (i InstrumentKind) String() string { if i < 0 || i >= InstrumentKind(len(_InstrumentKind_index)-1) { return "InstrumentKind(" + strconv.FormatInt(int64(i), 10) + ")" } return _InstrumentKind_name[_InstrumentKind_index[i]:_InstrumentKind_index[i+1]] } golang-opentelemetry-otel-1.1.0/metric/sdkapi/instrumentkind_test.go000066400000000000000000000024641414226744000260270ustar00rootroot00000000000000// 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 sdkapi_test import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/sdkapi" ) func TestInstrumentKinds(t *testing.T) { require.Equal(t, sdkapi.HistogramInstrumentKind.String(), "HistogramInstrumentKind") require.Equal(t, sdkapi.GaugeObserverInstrumentKind.String(), "GaugeObserverInstrumentKind") require.Equal(t, sdkapi.CounterInstrumentKind.String(), "CounterInstrumentKind") require.Equal(t, sdkapi.UpDownCounterInstrumentKind.String(), "UpDownCounterInstrumentKind") require.Equal(t, sdkapi.CounterObserverInstrumentKind.String(), "CounterObserverInstrumentKind") require.Equal(t, sdkapi.UpDownCounterObserverInstrumentKind.String(), "UpDownCounterObserverInstrumentKind") } golang-opentelemetry-otel-1.1.0/metric/sdkapi/noop.go000066400000000000000000000034741414226744000226670ustar00rootroot00000000000000// 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 sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" import ( "context" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" ) type noopInstrument struct{} type noopBoundInstrument struct{} type noopSyncInstrument struct{ noopInstrument } type noopAsyncInstrument struct{ noopInstrument } var _ SyncImpl = noopSyncInstrument{} var _ BoundSyncImpl = noopBoundInstrument{} var _ AsyncImpl = noopAsyncInstrument{} // NewNoopSyncInstrument returns a No-op implementation of the // synchronous instrument interface. func NewNoopSyncInstrument() SyncImpl { return noopSyncInstrument{} } // NewNoopSyncInstrument returns a No-op implementation of the // asynchronous instrument interface. func NewNoopAsyncInstrument() SyncImpl { return noopSyncInstrument{} } func (noopInstrument) Implementation() interface{} { return nil } func (noopInstrument) Descriptor() Descriptor { return Descriptor{} } func (noopBoundInstrument) RecordOne(context.Context, number.Number) { } func (noopBoundInstrument) Unbind() { } func (noopSyncInstrument) Bind([]attribute.KeyValue) BoundSyncImpl { return noopBoundInstrument{} } func (noopSyncInstrument) RecordOne(context.Context, number.Number, []attribute.KeyValue) { } golang-opentelemetry-otel-1.1.0/metric/sdkapi/sdkapi.go000066400000000000000000000133341414226744000231630ustar00rootroot00000000000000// 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 sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" import ( "context" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" ) // MeterImpl is the interface an SDK must implement to supply a Meter // implementation. type MeterImpl interface { // RecordBatch atomically records a batch of measurements. RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurement ...Measurement) // NewSyncInstrument returns a newly constructed // synchronous instrument implementation or an error, should // one occur. NewSyncInstrument(descriptor Descriptor) (SyncImpl, error) // NewAsyncInstrument returns a newly constructed // asynchronous instrument implementation or an error, should // one occur. NewAsyncInstrument( descriptor Descriptor, runner AsyncRunner, ) (AsyncImpl, error) } // InstrumentImpl is a common interface for synchronous and // asynchronous instruments. type InstrumentImpl interface { // Implementation returns the underlying implementation of the // instrument, which allows the implementation to gain access // to its own representation especially from a `Measurement`. Implementation() interface{} // Descriptor returns a copy of the instrument's Descriptor. Descriptor() Descriptor } // SyncImpl is the implementation-level interface to a generic // synchronous instrument (e.g., Histogram and Counter instruments). type SyncImpl interface { InstrumentImpl // Bind creates an implementation-level bound instrument, // binding a label set with this instrument implementation. Bind(labels []attribute.KeyValue) BoundSyncImpl // RecordOne captures a single synchronous metric event. RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) } // BoundSyncImpl is the implementation-level interface to a // generic bound synchronous instrument type BoundSyncImpl interface { // RecordOne captures a single synchronous metric event. RecordOne(ctx context.Context, number number.Number) // Unbind frees the resources associated with this bound instrument. It // does not affect the metric this bound instrument was created through. Unbind() } // AsyncImpl is an implementation-level interface to an // asynchronous instrument (e.g., Observer instruments). type AsyncImpl interface { InstrumentImpl } // AsyncRunner is expected to convert into an AsyncSingleRunner or an // AsyncBatchRunner. SDKs will encounter an error if the AsyncRunner // does not satisfy one of these interfaces. type AsyncRunner interface { // AnyRunner() is a non-exported method with no functional use // other than to make this a non-empty interface. AnyRunner() } // AsyncSingleRunner is an interface implemented by single-observer // callbacks. type AsyncSingleRunner interface { // Run accepts a single instrument and function for capturing // observations of that instrument. Each call to the function // receives one captured observation. (The function accepts // multiple observations so the same implementation can be // used for batch runners.) Run(ctx context.Context, single AsyncImpl, capture func([]attribute.KeyValue, ...Observation)) AsyncRunner } // AsyncBatchRunner is an interface implemented by batch-observer // callbacks. type AsyncBatchRunner interface { // Run accepts a function for capturing observations of // multiple instruments. Run(ctx context.Context, capture func([]attribute.KeyValue, ...Observation)) AsyncRunner } // NewMeasurement constructs a single observation, a binding between // an asynchronous instrument and a number. func NewMeasurement(instrument SyncImpl, number number.Number) Measurement { return Measurement{ instrument: instrument, number: number, } } // Measurement is a low-level type used with synchronous instruments // as a direct interface to the SDK via `RecordBatch`. type Measurement struct { // number needs to be aligned for 64-bit atomic operations. number number.Number instrument SyncImpl } // SyncImpl returns the instrument that created this measurement. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (m Measurement) SyncImpl() SyncImpl { return m.instrument } // Number returns a number recorded in this measurement. func (m Measurement) Number() number.Number { return m.number } // NewObservation constructs a single observation, a binding between // an asynchronous instrument and a number. func NewObservation(instrument AsyncImpl, number number.Number) Observation { return Observation{ instrument: instrument, number: number, } } // Observation is a low-level type used with asynchronous instruments // as a direct interface to the SDK via `BatchObserver`. type Observation struct { // number needs to be aligned for 64-bit atomic operations. number number.Number instrument AsyncImpl } // AsyncImpl returns the instrument that created this observation. // This returns an implementation-level object for use by the SDK, // users should not refer to this. func (m Observation) AsyncImpl() AsyncImpl { return m.instrument } // Number returns a number recorded in this observation. func (m Observation) Number() number.Number { return m.number } golang-opentelemetry-otel-1.1.0/metric/sdkapi/sdkapi_test.go000066400000000000000000000022221414226744000242140ustar00rootroot00000000000000// 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 sdkapi import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/number" ) func TestMeasurementGetters(t *testing.T) { num := number.NewFloat64Number(1.5) si := NewNoopSyncInstrument() meas := NewMeasurement(si, num) require.Equal(t, si, meas.SyncImpl()) require.Equal(t, num, meas.Number()) } func TestObservationGetters(t *testing.T) { num := number.NewFloat64Number(1.5) ai := NewNoopAsyncInstrument() obs := NewObservation(ai, num) require.Equal(t, ai, obs.AsyncImpl()) require.Equal(t, num, obs.Number()) } golang-opentelemetry-otel-1.1.0/metric/unit/000077500000000000000000000000001414226744000210615ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/metric/unit/doc.go000066400000000000000000000016231414226744000221570ustar00rootroot00000000000000// 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 unit provides units. // // This package is currently in a pre-GA phase. Backwards incompatible changes // may be introduced in subsequent minor version releases as we work to track // the evolving OpenTelemetry specification and user feedback. package unit // import "go.opentelemetry.io/otel/metric/unit" golang-opentelemetry-otel-1.1.0/metric/unit/unit.go000066400000000000000000000014441414226744000223720ustar00rootroot00000000000000// 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 unit // import "go.opentelemetry.io/otel/metric/unit" type Unit string // Units defined by OpenTelemetry. const ( Dimensionless Unit = "1" Bytes Unit = "By" Milliseconds Unit = "ms" ) golang-opentelemetry-otel-1.1.0/pre_release.sh000077500000000000000000000050341414226744000214460ustar00rootroot00000000000000#!/bin/bash # 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. set -e help() { printf "\n" printf "Usage: $0 -t tag\n" printf "\t-t Unreleased tag. Update all go.mod with this tag.\n" exit 1 # Exit script after printing help } while getopts "t:" opt do case "$opt" in t ) TAG="$OPTARG" ;; ? ) help ;; # Print help esac done # Print help in case parameters are empty if [ -z "$TAG" ] then printf "Tag is missing\n"; help fi # Validate semver SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" if [[ "${TAG}" =~ ${SEMVER_REGEX} ]]; then printf "${TAG} is valid semver tag.\n" else printf "${TAG} is not a valid semver tag.\n" exit -1 fi TAG_FOUND=`git tag --list ${TAG}` if [[ ${TAG_FOUND} = ${TAG} ]] ; then printf "Tag ${TAG} already exists\n" exit -1 fi # Get version for version.go OTEL_VERSION=$(echo "${TAG}" | grep -o '^v[0-9]\+\.[0-9]\+\.[0-9]\+') # Strip leading v OTEL_VERSION="${OTEL_VERSION#v}" cd $(dirname $0) if ! git diff --quiet; then \ printf "Working tree is not clean, can't proceed with the release process\n" git status git diff exit 1 fi # Update version.go cp ./version.go ./version.go.bak sed "s/\(return \"\)[0-9]*\.[0-9]*\.[0-9]*\"/\1${OTEL_VERSION}\"/" ./version.go.bak >./version.go rm -f ./version.go.bak # Update go.mod git checkout -b pre_release_${TAG} main PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep -v 'tools' | sed 's/^\.\///' | sort) for dir in $PACKAGE_DIRS; do cp "${dir}/go.mod" "${dir}/go.mod.bak" sed "s/opentelemetry.io\/otel\([^ ]*\) v[0-9]*\.[0-9]*\.[0-9]/opentelemetry.io\/otel\1 ${TAG}/" "${dir}/go.mod.bak" >"${dir}/go.mod" rm -f "${dir}/go.mod.bak" done # Run lint to update go.sum make lint # Add changes and commit. git add . make ci git commit -m "Prepare for releasing $TAG" printf "Now run following to verify the changes.\ngit diff main\n" printf "\nThen push the changes to upstream\n" golang-opentelemetry-otel-1.1.0/propagation.go000066400000000000000000000022031414226744000214660ustar00rootroot00000000000000// 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 otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/propagation" ) // GetTextMapPropagator returns the global TextMapPropagator. If none has been // set, a No-Op TextMapPropagator is returned. func GetTextMapPropagator() propagation.TextMapPropagator { return global.TextMapPropagator() } // SetTextMapPropagator sets propagator as the global TextMapPropagator. func SetTextMapPropagator(propagator propagation.TextMapPropagator) { global.SetTextMapPropagator(propagator) } golang-opentelemetry-otel-1.1.0/propagation/000077500000000000000000000000001414226744000211425ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/propagation/baggage.go000066400000000000000000000033341414226744000230510ustar00rootroot00000000000000// 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 propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "go.opentelemetry.io/otel/baggage" ) const baggageHeader = "baggage" // Baggage is a propagator that supports the W3C Baggage format. // // This propagates user-defined baggage associated with a trace. The complete // specification is defined at https://w3c.github.io/baggage/. type Baggage struct{} var _ TextMapPropagator = Baggage{} // Inject sets baggage key-values from ctx into the carrier. func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) { bStr := baggage.FromContext(ctx).String() if bStr != "" { carrier.Set(baggageHeader, bStr) } } // Extract returns a copy of parent with the baggage from the carrier added. func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context { bStr := carrier.Get(baggageHeader) if bStr == "" { return parent } bag, err := baggage.Parse(bStr) if err != nil { return parent } return baggage.ContextWithBaggage(parent, bag) } // Fields returns the keys who's values are set with Inject. func (b Baggage) Fields() []string { return []string{baggageHeader} } golang-opentelemetry-otel-1.1.0/propagation/baggage_test.go000066400000000000000000000132371414226744000241130ustar00rootroot00000000000000// 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 propagation_test import ( "context" "net/http" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/propagation" ) type property struct { Key, Value string } type member struct { Key, Value string Properties []property } func (m member) Member(t *testing.T) baggage.Member { props := make([]baggage.Property, 0, len(m.Properties)) for _, p := range m.Properties { p, err := baggage.NewKeyValueProperty(p.Key, p.Value) if err != nil { t.Fatal(err) } props = append(props, p) } bMember, err := baggage.NewMember(m.Key, m.Value, props...) if err != nil { t.Fatal(err) } return bMember } type members []member func (m members) Baggage(t *testing.T) baggage.Baggage { bMembers := make([]baggage.Member, 0, len(m)) for _, mem := range m { bMembers = append(bMembers, mem.Member(t)) } bag, err := baggage.New(bMembers...) if err != nil { t.Fatal(err) } return bag } func TestExtractValidBaggageFromHTTPReq(t *testing.T) { prop := propagation.TextMapPropagator(propagation.Baggage{}) tests := []struct { name string header string want members }{ { name: "valid w3cHeader", header: "key1=val1,key2=val2", want: members{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, }, }, { name: "valid w3cHeader with spaces", header: "key1 = val1, key2 =val2 ", want: members{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, }, }, { name: "valid w3cHeader with properties", header: "key1=val1,key2=val2;prop=1", want: members{ {Key: "key1", Value: "val1"}, { Key: "key2", Value: "val2", Properties: []property{ {Key: "prop", Value: "1"}, }, }, }, }, { name: "valid header with an invalid header", header: "key1=val1,key2=val2,a,val3", want: members{}, }, { name: "valid header with no value", header: "key1=,key2=val2", want: members{ {Key: "key1", Value: ""}, {Key: "key2", Value: "val2"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "http://example.com", nil) req.Header.Set("baggage", tt.header) ctx := context.Background() ctx = prop.Extract(ctx, propagation.HeaderCarrier(req.Header)) expected := tt.want.Baggage(t) assert.Equal(t, expected, baggage.FromContext(ctx)) }) } } func TestExtractInvalidDistributedContextFromHTTPReq(t *testing.T) { prop := propagation.TextMapPropagator(propagation.Baggage{}) tests := []struct { name string header string has members }{ { name: "no key values", header: "header1", }, { name: "invalid header with existing context", header: "header2", has: members{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, }, }, { name: "empty header value", header: "", has: members{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, }, }, { name: "with properties", header: "key1=val1,key2=val2;prop=1", has: members{ {Key: "key1", Value: "val1"}, { Key: "key2", Value: "val2", Properties: []property{ {Key: "prop", Value: "1"}, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "http://example.com", nil) req.Header.Set("baggage", tt.header) expected := tt.has.Baggage(t) ctx := baggage.ContextWithBaggage(context.Background(), expected) ctx = prop.Extract(ctx, propagation.HeaderCarrier(req.Header)) assert.Equal(t, expected, baggage.FromContext(ctx)) }) } } func TestInjectBaggageToHTTPReq(t *testing.T) { propagator := propagation.Baggage{} tests := []struct { name string mems members wantInHeader []string }{ { name: "two simple values", mems: members{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, }, wantInHeader: []string{"key1=val1", "key2=val2"}, }, { name: "values with escaped chars", mems: members{ {Key: "key2", Value: "val3=4"}, }, wantInHeader: []string{"key2=val3%3D4"}, }, { name: "with properties", mems: members{ {Key: "key1", Value: "val1"}, { Key: "key2", Value: "val2", Properties: []property{ {Key: "prop", Value: "1"}, }, }, }, wantInHeader: []string{ "key1=val1", "key2=val2;prop=1", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "http://example.com", nil) ctx := baggage.ContextWithBaggage(context.Background(), tt.mems.Baggage(t)) propagator.Inject(ctx, propagation.HeaderCarrier(req.Header)) got := strings.Split(req.Header.Get("baggage"), ",") assert.ElementsMatch(t, tt.wantInHeader, got) }) } } func TestBaggagePropagatorGetAllKeys(t *testing.T) { var propagator propagation.Baggage want := []string{"baggage"} got := propagator.Fields() if diff := cmp.Diff(got, want); diff != "" { t.Errorf("GetAllKeys: -got +want %s", diff) } } golang-opentelemetry-otel-1.1.0/propagation/doc.go000066400000000000000000000017771414226744000222520ustar00rootroot00000000000000// 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 propagation contains OpenTelemetry context propagators. OpenTelemetry propagators are used to extract and inject context data from and into messages exchanged by applications. The propagator supported by this package is the W3C Trace Context encoding (https://www.w3.org/TR/trace-context/), and W3C Baggage (https://w3c.github.io/baggage/). */ package propagation // import "go.opentelemetry.io/otel/propagation" golang-opentelemetry-otel-1.1.0/propagation/propagation.go000066400000000000000000000104721414226744000240200ustar00rootroot00000000000000// 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 propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "net/http" ) // TextMapCarrier is the storage medium used by a TextMapPropagator. type TextMapCarrier interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Get returns the value associated with the passed key. Get(key string) string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Set stores the key-value pair. Set(key string, value string) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Keys lists the keys stored in this carrier. Keys() []string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // HeaderCarrier adapts http.Header to satisfy the TextMapCarrier interface. type HeaderCarrier http.Header // Get returns the value associated with the passed key. func (hc HeaderCarrier) Get(key string) string { return http.Header(hc).Get(key) } // Set stores the key-value pair. func (hc HeaderCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) } // Keys lists the keys stored in this carrier. func (hc HeaderCarrier) Keys() []string { keys := make([]string, 0, len(hc)) for k := range hc { keys = append(keys, k) } return keys } // TextMapPropagator propagates cross-cutting concerns as key-value text // pairs within a carrier that travels in-band across process boundaries. type TextMapPropagator interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Inject set cross-cutting concerns from the Context into the carrier. Inject(ctx context.Context, carrier TextMapCarrier) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Extract reads cross-cutting concerns from the carrier into a Context. Extract(ctx context.Context, carrier TextMapCarrier) context.Context // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Fields returns the keys whose values are set with Inject. Fields() []string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } type compositeTextMapPropagator []TextMapPropagator func (p compositeTextMapPropagator) Inject(ctx context.Context, carrier TextMapCarrier) { for _, i := range p { i.Inject(ctx, carrier) } } func (p compositeTextMapPropagator) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { for _, i := range p { ctx = i.Extract(ctx, carrier) } return ctx } func (p compositeTextMapPropagator) Fields() []string { unique := make(map[string]struct{}) for _, i := range p { for _, k := range i.Fields() { unique[k] = struct{}{} } } fields := make([]string, 0, len(unique)) for k := range unique { fields = append(fields, k) } return fields } // NewCompositeTextMapPropagator returns a unified TextMapPropagator from the // group of passed TextMapPropagator. This allows different cross-cutting // concerns to be propagates in a unified manner. // // The returned TextMapPropagator will inject and extract cross-cutting // concerns in the order the TextMapPropagators were provided. Additionally, // the Fields method will return a de-duplicated slice of the keys that are // set with the Inject method. func NewCompositeTextMapPropagator(p ...TextMapPropagator) TextMapPropagator { return compositeTextMapPropagator(p) } golang-opentelemetry-otel-1.1.0/propagation/propagation_test.go000066400000000000000000000051571414226744000250630ustar00rootroot00000000000000// 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 propagation_test import ( "context" "strings" "testing" "go.opentelemetry.io/otel/propagation" ) type ctxKeyType uint var ( ctxKey ctxKeyType ) type carrier []string func (c *carrier) Keys() []string { return nil } func (c *carrier) Get(string) string { return "" } func (c *carrier) Set(setter, _ string) { *c = append(*c, setter) } type propagator struct { Name string } func (p propagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { carrier.Set(p.Name, "") } func (p propagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { v := ctx.Value(ctxKey) if v == nil { ctx = context.WithValue(ctx, ctxKey, []string{p.Name}) } else { orig := v.([]string) ctx = context.WithValue(ctx, ctxKey, append(orig, p.Name)) } return ctx } func (p propagator) Fields() []string { return []string{p.Name} } func TestCompositeTextMapPropagatorFields(t *testing.T) { a, b1, b2 := propagator{"a"}, propagator{"b"}, propagator{"b"} want := map[string]struct{}{ "a": {}, "b": {}, } got := propagation.NewCompositeTextMapPropagator(a, b1, b2).Fields() if len(got) != len(want) { t.Fatalf("invalid fields from composite: %v (want %v)", got, want) } for _, v := range got { if _, ok := want[v]; !ok { t.Errorf("invalid field returned from composite: %q", v) } } } func TestCompositeTextMapPropagatorInject(t *testing.T) { a, b := propagator{"a"}, propagator{"b"} c := make(carrier, 0, 2) propagation.NewCompositeTextMapPropagator(a, b).Inject(context.Background(), &c) if got := strings.Join([]string(c), ","); got != "a,b" { t.Errorf("invalid inject order: %s", got) } } func TestCompositeTextMapPropagatorExtract(t *testing.T) { a, b := propagator{"a"}, propagator{"b"} ctx := context.Background() ctx = propagation.NewCompositeTextMapPropagator(a, b).Extract(ctx, nil) v := ctx.Value(ctxKey) if v == nil { t.Fatal("no composite extraction") } if got := strings.Join(v.([]string), ","); got != "a,b" { t.Errorf("invalid extract order: %s", got) } } golang-opentelemetry-otel-1.1.0/propagation/propagators_test.go000066400000000000000000000064441414226744000251010ustar00rootroot00000000000000// 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 propagation_test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) const ( traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" spanIDStr = "00f067aa0ba902b7" ) var ( traceID = mustTraceIDFromHex(traceIDStr) spanID = mustSpanIDFromHex(spanIDStr) ) func mustTraceIDFromHex(s string) (t trace.TraceID) { var err error t, err = trace.TraceIDFromHex(s) if err != nil { panic(err) } return } func mustSpanIDFromHex(s string) (t trace.SpanID) { var err error t, err = trace.SpanIDFromHex(s) if err != nil { panic(err) } return } type outOfThinAirPropagator struct { t *testing.T } var _ propagation.TextMapPropagator = outOfThinAirPropagator{} func (p outOfThinAirPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: 0, }) require.True(p.t, sc.IsValid()) return trace.ContextWithRemoteSpanContext(ctx, sc) } func (outOfThinAirPropagator) Inject(context.Context, propagation.TextMapCarrier) {} func (outOfThinAirPropagator) Fields() []string { return nil } type nilCarrier struct{} var _ propagation.TextMapCarrier = nilCarrier{} func (nilCarrier) Keys() []string { return nil } func (nilCarrier) Get(key string) string { return "" } func (nilCarrier) Set(key string, value string) {} func TestMultiplePropagators(t *testing.T) { ootaProp := outOfThinAirPropagator{t: t} ns := nilCarrier{} testProps := []propagation.TextMapPropagator{ propagation.TraceContext{}, } bg := context.Background() // sanity check of oota propagator, ensuring that it really // generates the valid span context out of thin air { ctx := ootaProp.Extract(bg, ns) sc := trace.SpanContextFromContext(ctx) require.True(t, sc.IsValid(), "oota prop failed sanity check") require.True(t, sc.IsRemote(), "oota prop is remote") } // sanity check for real propagators, ensuring that they // really are not putting any valid span context into an empty // go context in absence of the HTTP headers. for _, prop := range testProps { ctx := prop.Extract(bg, ns) sc := trace.SpanContextFromContext(ctx) require.Falsef(t, sc.IsValid(), "%#v failed sanity check", prop) require.Falsef(t, sc.IsRemote(), "%#v prop set a remote", prop) } for _, prop := range testProps { props := propagation.NewCompositeTextMapPropagator(ootaProp, prop) ctx := props.Extract(bg, ns) sc := trace.SpanContextFromContext(ctx) assert.Truef(t, sc.IsRemote(), "%#v prop is remote", prop) assert.Truef(t, sc.IsValid(), "%#v clobbers span context", prop) } } golang-opentelemetry-otel-1.1.0/propagation/trace_context.go000066400000000000000000000107071414226744000243400ustar00rootroot00000000000000// 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 propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "encoding/hex" "fmt" "regexp" "go.opentelemetry.io/otel/trace" ) const ( supportedVersion = 0 maxVersion = 254 traceparentHeader = "traceparent" tracestateHeader = "tracestate" ) // TraceContext is a propagator that supports the W3C Trace Context format // (https://www.w3.org/TR/trace-context/) // // This propagator will propagate the traceparent and tracestate headers to // guarantee traces are not broken. It is up to the users of this propagator // to choose if they want to participate in a trace by modifying the // traceparent header and relevant parts of the tracestate header containing // their proprietary information. type TraceContext struct{} var _ TextMapPropagator = TraceContext{} var traceCtxRegExp = regexp.MustCompile("^(?P[0-9a-f]{2})-(?P[a-f0-9]{32})-(?P[a-f0-9]{16})-(?P[a-f0-9]{2})(?:-.*)?$") // Inject set tracecontext from the Context into the carrier. func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) { sc := trace.SpanContextFromContext(ctx) if !sc.IsValid() { return } if ts := sc.TraceState().String(); ts != "" { carrier.Set(tracestateHeader, ts) } // Clear all flags other than the trace-context supported sampling bit. flags := sc.TraceFlags() & trace.FlagsSampled h := fmt.Sprintf("%.2x-%s-%s-%s", supportedVersion, sc.TraceID(), sc.SpanID(), flags) carrier.Set(traceparentHeader, h) } // Extract reads tracecontext from the carrier into a returned Context. // // The returned Context will be a copy of ctx and contain the extracted // tracecontext as the remote SpanContext. If the extracted tracecontext is // invalid, the passed ctx will be returned directly instead. func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { sc := tc.extract(carrier) if !sc.IsValid() { return ctx } return trace.ContextWithRemoteSpanContext(ctx, sc) } func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext { h := carrier.Get(traceparentHeader) if h == "" { return trace.SpanContext{} } matches := traceCtxRegExp.FindStringSubmatch(h) if len(matches) == 0 { return trace.SpanContext{} } if len(matches) < 5 { // four subgroups plus the overall match return trace.SpanContext{} } if len(matches[1]) != 2 { return trace.SpanContext{} } ver, err := hex.DecodeString(matches[1]) if err != nil { return trace.SpanContext{} } version := int(ver[0]) if version > maxVersion { return trace.SpanContext{} } if version == 0 && len(matches) != 5 { // four subgroups plus the overall match return trace.SpanContext{} } if len(matches[2]) != 32 { return trace.SpanContext{} } var scc trace.SpanContextConfig scc.TraceID, err = trace.TraceIDFromHex(matches[2][:32]) if err != nil { return trace.SpanContext{} } if len(matches[3]) != 16 { return trace.SpanContext{} } scc.SpanID, err = trace.SpanIDFromHex(matches[3]) if err != nil { return trace.SpanContext{} } if len(matches[4]) != 2 { return trace.SpanContext{} } opts, err := hex.DecodeString(matches[4]) if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) { return trace.SpanContext{} } // Clear all flags other than the trace-context supported sampling bit. scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled // Ignore the error returned here. Failure to parse tracestate MUST NOT // affect the parsing of traceparent according to the W3C tracecontext // specification. scc.TraceState, _ = trace.ParseTraceState(carrier.Get(tracestateHeader)) scc.Remote = true sc := trace.NewSpanContext(scc) if !sc.IsValid() { return trace.SpanContext{} } return sc } // Fields returns the keys who's values are set with Inject. func (tc TraceContext) Fields() []string { return []string{traceparentHeader, tracestateHeader} } golang-opentelemetry-otel-1.1.0/propagation/trace_context_benchmark_test.go000066400000000000000000000050421414226744000274050ustar00rootroot00000000000000// 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 propagation_test import ( "context" "net/http" "testing" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) func BenchmarkInject(b *testing.B) { var t propagation.TraceContext injectSubBenchmarks(b, func(ctx context.Context, b *testing.B) { h := http.Header{} b.ResetTimer() for i := 0; i < b.N; i++ { t.Inject(ctx, propagation.HeaderCarrier(h)) } }) } func injectSubBenchmarks(b *testing.B, fn func(context.Context, *testing.B)) { b.Run("SampledSpanContext", func(b *testing.B) { b.ReportAllocs() sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) fn(ctx, b) }) b.Run("WithoutSpanContext", func(b *testing.B) { b.ReportAllocs() ctx := context.Background() fn(ctx, b) }) } func BenchmarkExtract(b *testing.B) { extractSubBenchmarks(b, func(b *testing.B, req *http.Request) { var propagator propagation.TraceContext ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { propagator.Extract(ctx, propagation.HeaderCarrier(req.Header)) } }) } func extractSubBenchmarks(b *testing.B, fn func(*testing.B, *http.Request)) { b.Run("Sampled", func(b *testing.B) { req, _ := http.NewRequest("GET", "http://example.com", nil) req.Header.Set("traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01") b.ReportAllocs() fn(b, req) }) b.Run("BogusVersion", func(b *testing.B) { req, _ := http.NewRequest("GET", "http://example.com", nil) req.Header.Set("traceparent", "qw-00000000000000000000000000000000-0000000000000000-01") b.ReportAllocs() fn(b, req) }) b.Run("FutureAdditionalData", func(b *testing.B) { req, _ := http.NewRequest("GET", "http://example.com", nil) req.Header.Set("traceparent", "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09-XYZxsf09") b.ReportAllocs() fn(b, req) }) } golang-opentelemetry-otel-1.1.0/propagation/trace_context_example_test.go000066400000000000000000000015231414226744000271060ustar00rootroot00000000000000// 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 propagation_test import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" ) func ExampleTraceContext() { tc := propagation.TraceContext{} // Register the TraceContext propagator globally. otel.SetTextMapPropagator(tc) } golang-opentelemetry-otel-1.1.0/propagation/trace_context_test.go000066400000000000000000000225561414226744000254040ustar00rootroot00000000000000// 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 propagation_test import ( "context" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) var ( traceparent = http.CanonicalHeaderKey("traceparent") tracestate = http.CanonicalHeaderKey("tracestate") prop = propagation.TraceContext{} ) type testcase struct { name string header http.Header sc trace.SpanContext } func TestExtractValidTraceContext(t *testing.T) { stateStr := "key1=value1,key2=value2" state, err := trace.ParseTraceState(stateStr) require.NoError(t, err) tests := []testcase{ { name: "not sampled", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "sampled", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, Remote: true, }), }, { name: "valid tracestate", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, tracestate: []string{stateStr}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceState: state, Remote: true, }), }, { name: "invalid tracestate perserves traceparent", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, tracestate: []string{"invalid$@#=invalid"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "future version not sampled", header: http.Header{ traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "future version sampled", header: http.Header{ traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, Remote: true, }), }, { name: "future version sample bit set", header: http.Header{ traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, Remote: true, }), }, { name: "future version sample bit not set", header: http.Header{ traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "future version additional data", header: http.Header{ traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "B3 format ending in dash", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "future version B3 format ending in dash", header: http.Header{ traceparent: []string{"03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() ctx = prop.Extract(ctx, propagation.HeaderCarrier(tc.header)) assert.Equal(t, tc.sc, trace.SpanContextFromContext(ctx)) }) } } func TestExtractInvalidTraceContextFromHTTPReq(t *testing.T) { tests := []struct { name string header string }{ { name: "wrong version length", header: "0000-00000000000000000000000000000000-0000000000000000-01", }, { name: "wrong trace ID length", header: "00-ab00000000000000000000000000000000-cd00000000000000-01", }, { name: "wrong span ID length", header: "00-ab000000000000000000000000000000-cd0000000000000000-01", }, { name: "wrong trace flag length", header: "00-ab000000000000000000000000000000-cd00000000000000-0100", }, { name: "bogus version", header: "qw-00000000000000000000000000000000-0000000000000000-01", }, { name: "bogus trace ID", header: "00-qw000000000000000000000000000000-cd00000000000000-01", }, { name: "bogus span ID", header: "00-ab000000000000000000000000000000-qw00000000000000-01", }, { name: "bogus trace flag", header: "00-ab000000000000000000000000000000-cd00000000000000-qw", }, { name: "upper case version", header: "A0-00000000000000000000000000000000-0000000000000000-01", }, { name: "upper case trace ID", header: "00-AB000000000000000000000000000000-cd00000000000000-01", }, { name: "upper case span ID", header: "00-ab000000000000000000000000000000-CD00000000000000-01", }, { name: "upper case trace flag", header: "00-ab000000000000000000000000000000-cd00000000000000-A1", }, { name: "zero trace ID and span ID", header: "00-00000000000000000000000000000000-0000000000000000-01", }, { name: "trace-flag unused bits set", header: "00-ab000000000000000000000000000000-cd00000000000000-09", }, { name: "missing options", header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7", }, { name: "empty options", header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-", }, } empty := trace.SpanContext{} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := http.Header{traceparent: []string{tt.header}} ctx := context.Background() ctx = prop.Extract(ctx, propagation.HeaderCarrier(h)) // Failure to extract needs to result in no SpanContext being set. // This cannot be directly measured, but we can check that an // zero-value SpanContext is returned from SpanContextFromContext. assert.Equal(t, empty, trace.SpanContextFromContext(ctx)) }) } } func TestInjectValidTraceContext(t *testing.T) { stateStr := "key1=value1,key2=value2" state, err := trace.ParseTraceState(stateStr) require.NoError(t, err) tests := []testcase{ { name: "not sampled", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: true, }), }, { name: "sampled", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, Remote: true, }), }, { name: "unsupported trace flag bits dropped", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: 0xff, Remote: true, }), }, { name: "with tracestate", header: http.Header{ traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"}, tracestate: []string{stateStr}, }, sc: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceState: state, Remote: true, }), }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() ctx = trace.ContextWithRemoteSpanContext(ctx, tc.sc) h := http.Header{} prop.Inject(ctx, propagation.HeaderCarrier(h)) assert.Equal(t, tc.header, h) }) } } func TestInvalidSpanContextDropped(t *testing.T) { invalidSC := trace.SpanContext{} require.False(t, invalidSC.IsValid()) ctx := trace.ContextWithRemoteSpanContext(context.Background(), invalidSC) header := http.Header{} propagation.TraceContext{}.Inject(ctx, propagation.HeaderCarrier(header)) assert.Equal(t, "", header.Get("traceparent"), "injected invalid SpanContext") } func TestTraceContextFields(t *testing.T) { expected := []string{"traceparent", "tracestate"} assert.Equal(t, expected, propagation.TraceContext{}.Fields()) } golang-opentelemetry-otel-1.1.0/schema/000077500000000000000000000000001414226744000200575ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/schema/README.md000066400000000000000000000017621414226744000213440ustar00rootroot00000000000000# Telemetry Schema Files The `schema` module contains packages that help to parse and validate [schema files](https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md). Each `major.minor` schema file format version is implemented as a separate package, with the name of the package in the `vmajor.minor` form. To parse a schema file, first decide what file format version you want to parse, then import the corresponding package and use the `Parse` or `ParseFile` functions like this: ```go import schema "go.opentelemetry.io/otel/schema/v1.0" // Load the schema from a file in v1.0.x file format. func loadSchemaFromFile() error { telSchema, err := schema.ParseFile("schema-file.yaml") if err != nil { return err } // Use telSchema struct here. } // Alternatively use schema.Parse to read the schema file from io.Reader. func loadSchemaFromReader(r io.Reader) error { telSchema, err := schema.Parse(r) if err != nil { return err } // Use telSchema struct here. } ``` golang-opentelemetry-otel-1.1.0/schema/go.mod000066400000000000000000000050751414226744000211740ustar00rootroot00000000000000module go.opentelemetry.io/otel/schema go 1.15 require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 ) replace go.opentelemetry.io/otel => ../ replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test replace go.opentelemetry.io/otel/bridge/opentracing => ../bridge/opentracing replace go.opentelemetry.io/otel/example/fib => ../example/fib replace go.opentelemetry.io/otel/example/jaeger => ../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../example/otel-collector replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough replace go.opentelemetry.io/otel/example/prometheus => ../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/jaeger => ../exporters/jaeger replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/prometheus => ../exporters/prometheus replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/zipkin => ../exporters/zipkin replace go.opentelemetry.io/otel/internal/metric => ../internal/metric replace go.opentelemetry.io/otel/internal/tools => ../internal/tools replace go.opentelemetry.io/otel/metric => ../metric replace go.opentelemetry.io/otel/schema => ./ replace go.opentelemetry.io/otel/sdk => ../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric replace go.opentelemetry.io/otel/trace => ../trace golang-opentelemetry-otel-1.1.0/schema/go.sum000066400000000000000000000025161414226744000212160ustar00rootroot00000000000000github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/schema/v1.0/000077500000000000000000000000001414226744000205435ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/000077500000000000000000000000001414226744000213325ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/ast_schema.go000066400000000000000000000042261414226744000237740ustar00rootroot00000000000000// 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 ast // import "go.opentelemetry.io/otel/schema/v1.0/ast" import "go.opentelemetry.io/otel/schema/v1.0/types" // Schema represents a Schema file in FileFormat 1.0.0 as defined in // https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md type Schema struct { // Schema file format. SHOULD be 1.0.0 for the current specification version. // See https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md#schema-file-format-number FileFormat string `yaml:"file_format"` // Schema URL is an identifier of a Schema. The URL specifies a location of this // Schema File that can be retrieved (so it is a URL and not just a URI) using HTTP // or HTTPS protocol. // See https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md#schema-url SchemaURL string `yaml:"schema_url"` // Versions section that lists changes that happened in each particular version. Versions map[types.TelemetryVersion]VersionDef } // VersionDef corresponds to a section representing one version under the "versions" // top-level key. type VersionDef struct { All Attributes Resources Attributes Spans Spans SpanEvents SpanEvents `yaml:"span_events"` Logs Logs Metrics Metrics } // Attributes corresponds to a section representing a list of changes that // happened in a particular version. type Attributes struct { Changes []AttributeChange } // AttributeChange corresponds to a section representing attribute changes. type AttributeChange struct { RenameAttributes *AttributeMap `yaml:"rename_attributes"` } golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/common.go000066400000000000000000000020761414226744000231560ustar00rootroot00000000000000// 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 ast // import "go.opentelemetry.io/otel/schema/v1.0/ast" // RenameAttributes corresponds to a section that describes attribute renaming. type RenameAttributes struct { AttributeMap AttributeMap `yaml:"attribute_map"` } // AttributeMap corresponds to a section representing a mapping of attribute names. // The keys are the old attribute name used in the previous version, the values are the // new attribute name starting from this version. type AttributeMap map[string]string golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/logs.go000066400000000000000000000017301414226744000226260ustar00rootroot00000000000000// 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 ast // import "go.opentelemetry.io/otel/schema/v1.0/ast" // Logs corresponds to a section representing a list of changes that happened // to logs schema in a particular version. type Logs struct { Changes []LogsChange } // LogsChange corresponds to a section representing logs change. type LogsChange struct { RenameAttributes *RenameAttributes `yaml:"rename_attributes"` } golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/metrics.go000066400000000000000000000026321414226744000233320ustar00rootroot00000000000000// 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 ast // import "go.opentelemetry.io/otel/schema/v1.0/ast" import "go.opentelemetry.io/otel/schema/v1.0/types" // Metrics corresponds to a section representing a list of changes that happened // to metrics schema in a particular version. type Metrics struct { Changes []MetricsChange } // MetricsChange corresponds to a section representing metrics change. type MetricsChange struct { RenameMetrics map[types.MetricName]types.MetricName `yaml:"rename_metrics"` RenameAttributes *AttributeMapForMetrics `yaml:"rename_attributes"` } // AttributeMapForMetrics corresponds to a section representing a translation of // attributes for specific metrics. type AttributeMapForMetrics struct { ApplyToMetrics []types.MetricName `yaml:"apply_to_metrics"` AttributeMap AttributeMap `yaml:"attribute_map"` } golang-opentelemetry-otel-1.1.0/schema/v1.0/ast/spans.go000066400000000000000000000043001414226744000230020ustar00rootroot00000000000000// 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 ast // import "go.opentelemetry.io/otel/schema/v1.0/ast" import "go.opentelemetry.io/otel/schema/v1.0/types" // Spans corresponds to a section representing a list of changes that happened // to spans schema in a particular version. type Spans struct { Changes []SpansChange } // SpanEvents corresponds to a section representing a list of changes that happened // to span events schema in a particular version. type SpanEvents struct { Changes []SpanEventsChange } // SpansChange corresponds to a section representing spans change. type SpansChange struct { RenameAttributes *AttributeMapForSpans `yaml:"rename_attributes"` } // AttributeMapForSpans corresponds to a section representing a translation of // attributes for specific spans. type AttributeMapForSpans struct { ApplyToSpans []types.SpanName `yaml:"apply_to_spans"` AttributeMap AttributeMap `yaml:"attribute_map"` } // SpanEventsChange corresponds to a section representing span events change. type SpanEventsChange struct { RenameEvents *RenameSpanEvents `yaml:"rename_events"` RenameAttributes *RenameSpanEventAttributes `yaml:"rename_attributes"` } // RenameSpanEvents corresponds to section representing a renaming of span events. type RenameSpanEvents struct { EventNameMap map[string]string `yaml:"name_map"` } // RenameSpanEventAttributes corresponds to section representing a renaming of // attributes of span events. type RenameSpanEventAttributes struct { ApplyToSpans []types.SpanName `yaml:"apply_to_spans"` ApplyToEvents []types.EventName `yaml:"apply_to_events"` AttributeMap AttributeMap `yaml:"attribute_map"` } golang-opentelemetry-otel-1.1.0/schema/v1.0/parser.go000066400000000000000000000062541414226744000223750ustar00rootroot00000000000000// 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 schema // import "go.opentelemetry.io/otel/schema/v1.0" import ( "fmt" "io" "net/url" "os" "strconv" "strings" "github.com/Masterminds/semver/v3" "gopkg.in/yaml.v2" "go.opentelemetry.io/otel/schema/v1.0/ast" ) // Major file version number that this library supports. const supportedFormatMajor = 1 // Maximum minor version number that this library supports. const supportedFormatMinor = 0 // Maximum major+minor version number that this library supports, as a string. var supportedFormatMajorMinor = strconv.Itoa(supportedFormatMajor) + "." + strconv.Itoa(supportedFormatMinor) // 1.0 // ParseFile a schema file. schemaFilePath is the file path. func ParseFile(schemaFilePath string) (*ast.Schema, error) { file, err := os.Open(schemaFilePath) if err != nil { return nil, err } return Parse(file) } // Parse a schema file. schemaFileContent is the readable content of the schema file. func Parse(schemaFileContent io.Reader) (*ast.Schema, error) { var ts ast.Schema d := yaml.NewDecoder(schemaFileContent) err := d.Decode(&ts) if err != nil { return nil, err } if err := checkFileFormatField(ts.FileFormat); err != nil { return nil, err } if strings.TrimSpace(ts.SchemaURL) == "" { return nil, fmt.Errorf("schema_url field is missing") } if _, err := url.Parse(ts.SchemaURL); err != nil { return nil, fmt.Errorf("invalid URL specified in schema_url field: %w", err) } return &ts, nil } // checkFileFormatField validates the file format field according to the rules here: // https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md#schema-file-format-number func checkFileFormatField(fileFormat string) error { // Verify that the version number in the file is a semver. fileFormatParsed, err := semver.StrictNewVersion(fileFormat) if err != nil { return fmt.Errorf( "invalid schema file format version number %q (expected semver): %w", fileFormat, err, ) } // Check that the major version number in the file is the same as what we expect. if fileFormatParsed.Major() != supportedFormatMajor { return fmt.Errorf( "this library cannot parse file formats with major version other than %v", supportedFormatMajor, ) } // Check that the file minor version number is not greater than // what is requested supports. if fileFormatParsed.Minor() > supportedFormatMinor { return fmt.Errorf( "unsupported schema file format minor version number, expected no newer than %v, got %v", supportedFormatMajorMinor+".x", fileFormat, ) } // Patch, prerelease and metadata version number does not matter, so we don't check it. return nil } golang-opentelemetry-otel-1.1.0/schema/v1.0/parser_test.go000066400000000000000000000125121414226744000234260ustar00rootroot00000000000000// 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 schema import ( "bytes" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/schema/v1.0/ast" "go.opentelemetry.io/otel/schema/v1.0/types" ) func TestParseSchemaFile(t *testing.T) { ts, err := ParseFile("testdata/valid-example.yaml") assert.NoError(t, err) assert.NotNil(t, ts) assert.EqualValues(t, &ast.Schema{ FileFormat: "1.0.0", SchemaURL: "https://opentelemetry.io/schemas/1.1.0", Versions: map[types.TelemetryVersion]ast.VersionDef{ "1.0.0": {}, "1.1.0": { All: ast.Attributes{ Changes: []ast.AttributeChange{ {RenameAttributes: &ast.AttributeMap{ "k8s.cluster.name": "kubernetes.cluster.name", "k8s.namespace.name": "kubernetes.namespace.name", "k8s.node.name": "kubernetes.node.name", "k8s.node.uid": "kubernetes.node.uid", "k8s.pod.name": "kubernetes.pod.name", "k8s.pod.uid": "kubernetes.pod.uid", "k8s.container.name": "kubernetes.container.name", "k8s.replicaset.name": "kubernetes.replicaset.name", "k8s.replicaset.uid": "kubernetes.replicaset.uid", "k8s.cronjob.name": "kubernetes.cronjob.name", "k8s.cronjob.uid": "kubernetes.cronjob.uid", "k8s.job.name": "kubernetes.job.name", "k8s.job.uid": "kubernetes.job.uid", "k8s.statefulset.name": "kubernetes.statefulset.name", "k8s.statefulset.uid": "kubernetes.statefulset.uid", "k8s.daemonset.name": "kubernetes.daemonset.name", "k8s.daemonset.uid": "kubernetes.daemonset.uid", "k8s.deployment.name": "kubernetes.deployment.name", "k8s.deployment.uid": "kubernetes.deployment.uid", "service.namespace": "service.namespace.name", }}, }, }, Resources: ast.Attributes{ Changes: []ast.AttributeChange{ { RenameAttributes: &ast.AttributeMap{ "telemetry.auto.version": "telemetry.auto_instr.version", }, }, }, }, Spans: ast.Spans{ Changes: []ast.SpansChange{ { RenameAttributes: &ast.AttributeMapForSpans{ AttributeMap: ast.AttributeMap{ "peer.service": "peer.service.name", }, ApplyToSpans: []types.SpanName{"HTTP GET"}, }, }, }, }, SpanEvents: ast.SpanEvents{ Changes: []ast.SpanEventsChange{ { RenameEvents: &ast.RenameSpanEvents{ EventNameMap: map[string]string{ "exception.stacktrace": "exception.stack_trace", }, }, }, { RenameAttributes: &ast.RenameSpanEventAttributes{ ApplyToEvents: []types.EventName{"exception.stack_trace"}, AttributeMap: ast.AttributeMap{ "peer.service": "peer.service.name", }, }, }, }, }, Logs: ast.Logs{Changes: []ast.LogsChange{ {RenameAttributes: &ast.RenameAttributes{ AttributeMap: map[string]string{ "process.executable_name": "process.executable.name", }, }}, }}, Metrics: ast.Metrics{ Changes: []ast.MetricsChange{ { RenameAttributes: &ast.AttributeMapForMetrics{ AttributeMap: map[string]string{ "http.status_code": "http.response_status_code", }, }}, { RenameMetrics: map[types.MetricName]types.MetricName{ "container.cpu.usage.total": "cpu.usage.total", "container.memory.usage.max": "memory.usage.max", }, }, { RenameAttributes: &ast.AttributeMapForMetrics{ ApplyToMetrics: []types.MetricName{ "system.cpu.utilization", "system.memory.usage", "system.memory.utilization", "system.paging.usage", }, AttributeMap: map[string]string{ "status": "state", }, }, }, }, }, }, }, }, ts) } func TestFailParseSchemaFile(t *testing.T) { ts, err := ParseFile("testdata/unsupported-file-format.yaml") assert.Error(t, err) assert.Nil(t, ts) ts, err = ParseFile("testdata/invalid-schema-url.yaml") assert.Error(t, err) assert.Nil(t, ts) } func TestFailParseSchema(t *testing.T) { _, err := Parse(bytes.NewReader([]byte(""))) assert.Error(t, err) _, err = Parse(bytes.NewReader([]byte("invalid yaml"))) assert.Error(t, err) _, err = Parse(bytes.NewReader([]byte("file_format: 1.0.0"))) assert.Error(t, err) } func TestCheckFileFormatField(t *testing.T) { // Invalid file format version numbers. assert.Error(t, checkFileFormatField("not a semver")) assert.Error(t, checkFileFormatField("2.0.0")) assert.Error(t, checkFileFormatField("1.1.0")) // Valid cases. assert.NoError(t, checkFileFormatField("1.0.0")) assert.NoError(t, checkFileFormatField("1.0.1")) assert.NoError(t, checkFileFormatField("1.0.10000-alpha+4857")) } golang-opentelemetry-otel-1.1.0/schema/v1.0/testdata/000077500000000000000000000000001414226744000223545ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/schema/v1.0/testdata/invalid-schema-url.yaml000066400000000000000000000000761414226744000267270ustar00rootroot00000000000000file_format: 1.0.0 schema_url: http://invalid url versions: golang-opentelemetry-otel-1.1.0/schema/v1.0/testdata/unsupported-file-format.yaml000066400000000000000000000002331414226744000300310ustar00rootroot00000000000000file_format: 1.1.0 versions: 1.1.0: all: changes: - rename_attributes: k8s.cluster.name: kubernetes.cluster.name 1.0.0: golang-opentelemetry-otel-1.1.0/schema/v1.0/testdata/valid-example.yaml000066400000000000000000000133601414226744000257730ustar00rootroot00000000000000file_format: 1.0.0 schema_url: https://opentelemetry.io/schemas/1.1.0 versions: 1.1.0: # Section "all" applies to attributes names for all data types: resources, spans, logs, # span events, metric labels. # # The translations in "all" section are performed first (for each particular version). # Only after that the translations in the specific section ("resources", "traces", # "metrics" or "logs") that corresponds to the data type are applied. # # The only translation possible in section "all" is renaming of attributes in # versions. For human readability versions are listed in reverse chronological # order, however note that the translations are applied in the order defined by # semver ordering. all: changes: - rename_attributes: # Mapping of attribute names (label names for metrics). The key is the old name # used prior to this version, the value is the new name starting from this version. # Rename k8s.* to kubernetes.* k8s.cluster.name: kubernetes.cluster.name k8s.namespace.name: kubernetes.namespace.name k8s.node.name: kubernetes.node.name k8s.node.uid: kubernetes.node.uid k8s.pod.name: kubernetes.pod.name k8s.pod.uid: kubernetes.pod.uid k8s.container.name: kubernetes.container.name k8s.replicaset.name: kubernetes.replicaset.name k8s.replicaset.uid: kubernetes.replicaset.uid k8s.cronjob.name: kubernetes.cronjob.name k8s.cronjob.uid: kubernetes.cronjob.uid k8s.job.name: kubernetes.job.name k8s.job.uid: kubernetes.job.uid k8s.statefulset.name: kubernetes.statefulset.name k8s.statefulset.uid: kubernetes.statefulset.uid k8s.daemonset.name: kubernetes.daemonset.name k8s.daemonset.uid: kubernetes.daemonset.uid k8s.deployment.name: kubernetes.deployment.name k8s.deployment.uid: kubernetes.deployment.uid service.namespace: service.namespace.name # Like "all" the "resources" section may contain only attribute renaming translations. # The only translation possible in this section is renaming of attributes in # versions. resources: changes: - rename_attributes: # Mapping of attribute names. The key is the old name # used prior to this version, the value is the new name starting from this version. telemetry.auto.version: telemetry.auto_instr.version spans: changes: # Sequence of translations to apply to convert the schema from a prior version # to this version. The order in this sequence is important. Translations are # applied from top to bottom in the listed order. - rename_attributes: # Rename attributes of all spans, regardless of span name. # The keys are the old attribute name used prior to this version, the values are # the new attribute name starting from this version. attribute_map: peer.service: peer.service.name apply_to_spans: # apply only to spans named "HTTP GET" - "HTTP GET" span_events: changes: # Sequence of translations to apply to convert the schema from a prior version # to this version. The order in this sequence is important. Translations are # applied from top to bottom in the listed order. - rename_events: # Rename events. The keys are old event names, the values are the new event names. name_map: {exception.stacktrace: exception.stack_trace} - rename_attributes: # Rename attributes of events. # The keys are the old attribute name used prior to this version, the values are # the new attribute name starting from this version. attribute_map: peer.service: peer.service.name apply_to_events: # Optional event names to apply to. If empty applies to all events. # Conditions in apply_to_spans and apply_to_events are logical AND-ed, # both should match for transformation to be applied. - exception.stack_trace metrics: changes: # Sequence of translations to apply to convert the schema from a prior version # to this version. The order in this sequence is important. Translations are # applied from top to bottom in the listed order. - rename_attributes: # Rename attributes of all metrics, regardless of metric name. # The keys are the old attribute name used prior to this version, the values are # the new attribute name starting from this version. attribute_map: http.status_code: http.response_status_code - rename_metrics: # Rename metrics. The keys are old metric names, the values are the new metric names. container.cpu.usage.total: cpu.usage.total container.memory.usage.max: memory.usage.max - rename_attributes: apply_to_metrics: # Name of the metric to apply this rule to. If empty the rule applies to all metrics. - system.cpu.utilization - system.memory.usage - system.memory.utilization - system.paging.usage attribute_map: # The keys are the old attribute name used prior to this version, the values are # the new attribute name starting from this version. status: state logs: changes: - rename_attributes: attribute_map: process.executable_name: process.executable.name 1.0.0: golang-opentelemetry-otel-1.1.0/schema/v1.0/types/000077500000000000000000000000001414226744000217075ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/schema/v1.0/types/types.go000066400000000000000000000016721414226744000234100ustar00rootroot00000000000000// 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 types // import "go.opentelemetry.io/otel/schema/v1.0/types" // TelemetryVersion is a version number key in the schema file (e.g. "1.7.0") type TelemetryVersion string // SpanName is span name string. type SpanName string // EventName is an event name string. type EventName string // MetricName is a metric name string. type MetricName string golang-opentelemetry-otel-1.1.0/sdk/000077500000000000000000000000001414226744000174005ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/export/000077500000000000000000000000001414226744000207215ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/export/metric/000077500000000000000000000000001414226744000222045ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/export/metric/aggregation/000077500000000000000000000000001414226744000244735ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/export/metric/aggregation/aggregation.go000066400000000000000000000107171414226744000273170ustar00rootroot00000000000000// 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 aggregation // import "go.opentelemetry.io/otel/sdk/export/metric/aggregation" import ( "fmt" "time" "go.opentelemetry.io/otel/metric/number" ) // These interfaces describe the various ways to access state from an // Aggregation. type ( // Aggregation is an interface returned by the Aggregator // containing an interval of metric data. Aggregation interface { // Kind returns a short identifying string to identify // the Aggregator that was used to produce the // Aggregation (e.g., "Sum"). Kind() Kind } // Sum returns an aggregated sum. Sum interface { Aggregation Sum() (number.Number, error) } // Count returns the number of values that were aggregated. Count interface { Aggregation Count() (uint64, error) } // Min returns the minimum value over the set of values that were aggregated. Min interface { Aggregation Min() (number.Number, error) } // Max returns the maximum value over the set of values that were aggregated. Max interface { Aggregation Max() (number.Number, error) } // LastValue returns the latest value that was aggregated. LastValue interface { Aggregation LastValue() (number.Number, time.Time, error) } // Points returns the raw values that were aggregated. Points interface { Aggregation // Points returns points in the order they were // recorded. Points are approximately ordered by // timestamp, but this is not guaranteed. Points() ([]Point, error) } // Point is a raw data point, consisting of a number and value. Point struct { number.Number time.Time } // Buckets represents histogram buckets boundaries and counts. // // For a Histogram with N defined boundaries, e.g, [x, y, z]. // There are N+1 counts: [-inf, x), [x, y), [y, z), [z, +inf] Buckets struct { // Boundaries are floating point numbers, even when // aggregating integers. Boundaries []float64 // Counts holds the count in each bucket. Counts []uint64 } // Histogram returns the count of events in pre-determined buckets. Histogram interface { Aggregation Count() (uint64, error) Sum() (number.Number, error) Histogram() (Buckets, error) } // MinMaxSumCount supports the Min, Max, Sum, and Count interfaces. MinMaxSumCount interface { Aggregation Min() (number.Number, error) Max() (number.Number, error) Sum() (number.Number, error) Count() (uint64, error) } ) type ( // Kind is a short name for the Aggregator that produces an // Aggregation, used for descriptive purpose only. Kind is a // string to allow user-defined Aggregators. // // When deciding how to handle an Aggregation, Exporters are // encouraged to decide based on conversion to the above // interfaces based on strength, not on Kind value, when // deciding how to expose metric data. This enables // user-supplied Aggregators to replace builtin Aggregators. // // For example, test for a Distribution before testing for a // MinMaxSumCount, test for a Histogram before testing for a // Sum, and so on. Kind string ) // Kind description constants. const ( SumKind Kind = "Sum" MinMaxSumCountKind Kind = "MinMaxSumCount" HistogramKind Kind = "Histogram" LastValueKind Kind = "Lastvalue" ExactKind Kind = "Exact" ) // Sentinel errors for Aggregation interface. var ( ErrNegativeInput = fmt.Errorf("negative value is out of range for this instrument") ErrNaNInput = fmt.Errorf("NaN value is an invalid input") ErrInconsistentType = fmt.Errorf("inconsistent aggregator types") ErrNoSubtraction = fmt.Errorf("aggregator does not subtract") // ErrNoData is returned when (due to a race with collection) // the Aggregator is check-pointed before the first value is set. // The aggregator should simply be skipped in this case. ErrNoData = fmt.Errorf("no data collected by this aggregator") ) // String returns the string value of Kind. func (k Kind) String() string { return string(k) } golang-opentelemetry-otel-1.1.0/sdk/export/metric/aggregation/temporality.go000066400000000000000000000100141414226744000273670ustar00rootroot00000000000000// 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. //go:generate stringer -type=Temporality package aggregation // import "go.opentelemetry.io/otel/sdk/export/metric/aggregation" import ( "go.opentelemetry.io/otel/metric/sdkapi" ) // Temporality indicates the temporal aggregation exported by an exporter. // These bits may be OR-d together when multiple exporters are in use. type Temporality uint8 const ( // CumulativeTemporality indicates that an Exporter expects a // Cumulative Aggregation. CumulativeTemporality Temporality = 1 // DeltaTemporality indicates that an Exporter expects a // Delta Aggregation. DeltaTemporality Temporality = 2 ) // Includes returns if t includes support for other temporality. func (t Temporality) Includes(other Temporality) bool { return t&other != 0 } // MemoryRequired returns whether an exporter of this temporality requires // memory to export correctly. func (t Temporality) MemoryRequired(mkind sdkapi.InstrumentKind) bool { switch mkind { case sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, sdkapi.CounterInstrumentKind, sdkapi.UpDownCounterInstrumentKind: // Delta-oriented instruments: return t.Includes(CumulativeTemporality) case sdkapi.CounterObserverInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind: // Cumulative-oriented instruments: return t.Includes(DeltaTemporality) } // Something unexpected is happening--we could panic. This // will become an error when the exporter tries to access a // checkpoint, presumably, so let it be. return false } type ( constantTemporalitySelector Temporality statelessTemporalitySelector struct{} ) var ( _ TemporalitySelector = constantTemporalitySelector(0) _ TemporalitySelector = statelessTemporalitySelector{} ) // ConstantTemporalitySelector returns an TemporalitySelector that returns // a constant Temporality. func ConstantTemporalitySelector(t Temporality) TemporalitySelector { return constantTemporalitySelector(t) } // CumulativeTemporalitySelector returns an TemporalitySelector that // always returns CumulativeTemporality. func CumulativeTemporalitySelector() TemporalitySelector { return ConstantTemporalitySelector(CumulativeTemporality) } // DeltaTemporalitySelector returns an TemporalitySelector that // always returns DeltaTemporality. func DeltaTemporalitySelector() TemporalitySelector { return ConstantTemporalitySelector(DeltaTemporality) } // StatelessTemporalitySelector returns an TemporalitySelector that // always returns the Temporality that avoids long-term memory // requirements. func StatelessTemporalitySelector() TemporalitySelector { return statelessTemporalitySelector{} } // TemporalityFor implements TemporalitySelector. func (c constantTemporalitySelector) TemporalityFor(_ *sdkapi.Descriptor, _ Kind) Temporality { return Temporality(c) } // TemporalityFor implements TemporalitySelector. func (s statelessTemporalitySelector) TemporalityFor(desc *sdkapi.Descriptor, kind Kind) Temporality { if kind == SumKind && desc.InstrumentKind().PrecomputedSum() { return CumulativeTemporality } return DeltaTemporality } // TemporalitySelector is a sub-interface of Exporter used to indicate // whether the Processor should compute Delta or Cumulative // Aggregations. type TemporalitySelector interface { // TemporalityFor should return the correct Temporality that // should be used when exporting data for the given metric // instrument and Aggregator kind. TemporalityFor(descriptor *sdkapi.Descriptor, aggregationKind Kind) Temporality } golang-opentelemetry-otel-1.1.0/sdk/export/metric/aggregation/temporality_string.go000066400000000000000000000012751414226744000307660ustar00rootroot00000000000000// Code generated by "stringer -type=Temporality"; DO NOT EDIT. package aggregation import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[CumulativeTemporality-1] _ = x[DeltaTemporality-2] } const _Temporality_name = "CumulativeTemporalityDeltaTemporality" var _Temporality_index = [...]uint8{0, 21, 37} func (i Temporality) String() string { i -= 1 if i >= Temporality(len(_Temporality_index)-1) { return "Temporality(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _Temporality_name[_Temporality_index[i]:_Temporality_index[i+1]] } golang-opentelemetry-otel-1.1.0/sdk/export/metric/aggregation/temporality_test.go000066400000000000000000000046131414226744000304360ustar00rootroot00000000000000// 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 aggregation import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" ) func TestTemporalityIncludes(t *testing.T) { require.True(t, CumulativeTemporality.Includes(CumulativeTemporality)) require.True(t, DeltaTemporality.Includes(CumulativeTemporality|DeltaTemporality)) } var deltaMemoryTemporalties = []sdkapi.InstrumentKind{ sdkapi.CounterObserverInstrumentKind, sdkapi.UpDownCounterObserverInstrumentKind, } var cumulativeMemoryTemporalties = []sdkapi.InstrumentKind{ sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, sdkapi.CounterInstrumentKind, sdkapi.UpDownCounterInstrumentKind, } func TestTemporalityMemoryRequired(t *testing.T) { for _, kind := range deltaMemoryTemporalties { require.True(t, DeltaTemporality.MemoryRequired(kind)) require.False(t, CumulativeTemporality.MemoryRequired(kind)) } for _, kind := range cumulativeMemoryTemporalties { require.True(t, CumulativeTemporality.MemoryRequired(kind)) require.False(t, DeltaTemporality.MemoryRequired(kind)) } } func TestTemporalitySelectors(t *testing.T) { cAggTemp := CumulativeTemporalitySelector() dAggTemp := DeltaTemporalitySelector() sAggTemp := StatelessTemporalitySelector() for _, ikind := range append(deltaMemoryTemporalties, cumulativeMemoryTemporalties...) { desc := metrictest.NewDescriptor("instrument", ikind, number.Int64Kind) var akind Kind if ikind.Adding() { akind = SumKind } else { akind = HistogramKind } require.Equal(t, CumulativeTemporality, cAggTemp.TemporalityFor(&desc, akind)) require.Equal(t, DeltaTemporality, dAggTemp.TemporalityFor(&desc, akind)) require.False(t, sAggTemp.TemporalityFor(&desc, akind).MemoryRequired(ikind)) } } golang-opentelemetry-otel-1.1.0/sdk/export/metric/go.mod000066400000000000000000000055531414226744000233220ustar00rootroot00000000000000module go.opentelemetry.io/otel/sdk/export/metric go 1.15 replace go.opentelemetry.io/otel => ../../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../../internal/tools replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/sdk => ../.. replace go.opentelemetry.io/otel/sdk/export/metric => ./ replace go.opentelemetry.io/otel/sdk/metric => ../../metric replace go.opentelemetry.io/otel/trace => ../../../trace require ( github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 ) replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../../example/fib replace go.opentelemetry.io/otel/schema => ../../../schema golang-opentelemetry-otel-1.1.0/sdk/export/metric/go.sum000066400000000000000000000031151414226744000233370ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/sdk/export/metric/metric.go000066400000000000000000000325011414226744000240170ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/sdk/export/metric" import ( "context" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) // Processor is responsible for deciding which kind of aggregation to // use (via AggregatorSelector), gathering exported results from the // SDK during collection, and deciding over which dimensions to group // the exported data. // // The SDK supports binding only one of these interfaces, as it has // the sole responsibility of determining which Aggregator to use for // each record. // // The embedded AggregatorSelector interface is called (concurrently) // in instrumentation context to select the appropriate Aggregator for // an instrument. // // The `Process` method is called during collection in a // single-threaded context from the SDK, after the aggregator is // checkpointed, allowing the processor to build the set of metrics // currently being exported. type Processor interface { // AggregatorSelector is responsible for selecting the // concrete type of Aggregator used for a metric in the SDK. // // This may be a static decision based on fields of the // Descriptor, or it could use an external configuration // source to customize the treatment of each metric // instrument. // // The result from AggregatorSelector.AggregatorFor should be // the same type for a given Descriptor or else nil. The same // type should be returned for a given descriptor, because // Aggregators only know how to Merge with their own type. If // the result is nil, the metric instrument will be disabled. // // Note that the SDK only calls AggregatorFor when new records // require an Aggregator. This does not provide a way to // disable metrics with active records. AggregatorSelector // Process is called by the SDK once per internal record, // passing the export Accumulation (a Descriptor, the corresponding // Labels, and the checkpointed Aggregator). This call has no // Context argument because it is expected to perform only // computation. An SDK is not expected to call exporters from // with Process, use a controller for that (see // ./controllers/{pull,push}. Process(accum Accumulation) error } // AggregatorSelector supports selecting the kind of Aggregator to // use at runtime for a specific metric instrument. type AggregatorSelector interface { // AggregatorFor allocates a variable number of aggregators of // a kind suitable for the requested export. This method // initializes a `...*Aggregator`, to support making a single // allocation. // // When the call returns without initializing the *Aggregator // to a non-nil value, the metric instrument is explicitly // disabled. // // This must return a consistent type to avoid confusion in // later stages of the metrics export process, i.e., when // Merging or Checkpointing aggregators for a specific // instrument. // // Note: This is context-free because the aggregator should // not relate to the incoming context. This call should not // block. AggregatorFor(descriptor *sdkapi.Descriptor, aggregator ...*Aggregator) } // Checkpointer is the interface used by a Controller to coordinate // the Processor with Accumulator(s) and Exporter(s). The // StartCollection() and FinishCollection() methods start and finish a // collection interval. Controllers call the Accumulator(s) during // collection to process Accumulations. type Checkpointer interface { // Processor processes metric data for export. The Process // method is bracketed by StartCollection and FinishCollection // calls. The embedded AggregatorSelector can be called at // any time. Processor // Reader returns the current data set. This may be // called before and after collection. The // implementation is required to return the same value // throughout its lifetime, since Reader exposes a // sync.Locker interface. The caller is responsible for // locking the Reader before initiating collection. Reader() Reader // StartCollection begins a collection interval. StartCollection() // FinishCollection ends a collection interval. FinishCollection() error } // CheckpointerFactory is an interface for producing configured // Checkpointer instances. type CheckpointerFactory interface { NewCheckpointer() Checkpointer } // Aggregator implements a specific aggregation behavior, e.g., a // behavior to track a sequence of updates to an instrument. Counter // instruments commonly use a simple Sum aggregator, but for the // distribution instruments (Histogram, GaugeObserver) there are a // number of possible aggregators with different cost and accuracy // tradeoffs. // // Note that any Aggregator may be attached to any instrument--this is // the result of the OpenTelemetry API/SDK separation. It is possible // to attach a Sum aggregator to a Histogram instrument or a // MinMaxSumCount aggregator to a Counter instrument. type Aggregator interface { // Aggregation returns an Aggregation interface to access the // current state of this Aggregator. The caller is // responsible for synchronization and must not call any the // other methods in this interface concurrently while using // the Aggregation. Aggregation() aggregation.Aggregation // Update receives a new measured value and incorporates it // into the aggregation. Update() calls may be called // concurrently. // // Descriptor.NumberKind() should be consulted to determine // whether the provided number is an int64 or float64. // // The Context argument comes from user-level code and could be // inspected for a `correlation.Map` or `trace.SpanContext`. Update(ctx context.Context, number number.Number, descriptor *sdkapi.Descriptor) error // SynchronizedMove is called during collection to finish one // period of aggregation by atomically saving the // currently-updating state into the argument Aggregator AND // resetting the current value to the zero state. // // SynchronizedMove() is called concurrently with Update(). These // two methods must be synchronized with respect to each // other, for correctness. // // After saving a synchronized copy, the Aggregator can be converted // into one or more of the interfaces in the `aggregation` sub-package, // according to kind of Aggregator that was selected. // // This method will return an InconsistentAggregatorError if // this Aggregator cannot be copied into the destination due // to an incompatible type. // // This call has no Context argument because it is expected to // perform only computation. // // When called with a nil `destination`, this Aggregator is reset // and the current value is discarded. SynchronizedMove(destination Aggregator, descriptor *sdkapi.Descriptor) error // Merge combines the checkpointed state from the argument // Aggregator into this Aggregator. Merge is not synchronized // with respect to Update or SynchronizedMove. // // The owner of an Aggregator being merged is responsible for // synchronization of both Aggregator states. Merge(aggregator Aggregator, descriptor *sdkapi.Descriptor) error } // Subtractor is an optional interface implemented by some // Aggregators. An Aggregator must support `Subtract()` in order to // be configured for a Precomputed-Sum instrument (CounterObserver, // UpDownCounterObserver) using a DeltaExporter. type Subtractor interface { // Subtract subtracts the `operand` from this Aggregator and // outputs the value in `result`. Subtract(operand, result Aggregator, descriptor *sdkapi.Descriptor) error } // Exporter handles presentation of the checkpoint of aggregate // metrics. This is the final stage of a metrics export pipeline, // where metric data are formatted for a specific system. type Exporter interface { // Export is called immediately after completing a collection // pass in the SDK. // // The Context comes from the controller that initiated // collection. // // The InstrumentationLibraryReader interface refers to the // Processor that just completed collection. Export(ctx context.Context, resource *resource.Resource, reader InstrumentationLibraryReader) error // TemporalitySelector is an interface used by the Processor // in deciding whether to compute Delta or Cumulative // Aggregations when passing Records to this Exporter. aggregation.TemporalitySelector } // InstrumentationLibraryReader is an interface for exporters to iterate // over one instrumentation library of metric data at a time. type InstrumentationLibraryReader interface { // ForEach calls the passed function once per instrumentation library, // allowing the caller to emit metrics grouped by the library that // produced them. ForEach(readerFunc func(instrumentation.Library, Reader) error) error } // Reader allows a controller to access a complete checkpoint of // aggregated metrics from the Processor for a single library of // metric data. This is passed to the Exporter which may then use // ForEach to iterate over the collection of aggregated metrics. type Reader interface { // ForEach iterates over aggregated checkpoints for all // metrics that were updated during the last collection // period. Each aggregated checkpoint returned by the // function parameter may return an error. // // The TemporalitySelector argument is used to determine // whether the Record is computed using Delta or Cumulative // aggregation. // // ForEach tolerates ErrNoData silently, as this is // expected from the Meter implementation. Any other kind // of error will immediately halt ForEach and return // the error to the caller. ForEach(tempSelector aggregation.TemporalitySelector, recordFunc func(Record) error) error // Locker supports locking the checkpoint set. Collection // into the checkpoint set cannot take place (in case of a // stateful processor) while it is locked. // // The Processor attached to the Accumulator MUST be called // with the lock held. sync.Locker // RLock acquires a read lock corresponding to this Locker. RLock() // RUnlock releases a read lock corresponding to this Locker. RUnlock() } // Metadata contains the common elements for exported metric data that // are shared by the Accumulator->Processor and Processor->Exporter // steps. type Metadata struct { descriptor *sdkapi.Descriptor labels *attribute.Set } // Accumulation contains the exported data for a single metric instrument // and label set, as prepared by an Accumulator for the Processor. type Accumulation struct { Metadata aggregator Aggregator } // Record contains the exported data for a single metric instrument // and label set, as prepared by the Processor for the Exporter. // This includes the effective start and end time for the aggregation. type Record struct { Metadata aggregation aggregation.Aggregation start time.Time end time.Time } // Descriptor describes the metric instrument being exported. func (m Metadata) Descriptor() *sdkapi.Descriptor { return m.descriptor } // Labels describes the labels associated with the instrument and the // aggregated data. func (m Metadata) Labels() *attribute.Set { return m.labels } // NewAccumulation allows Accumulator implementations to construct new // Accumulations to send to Processors. The Descriptor, Labels, // and Aggregator represent aggregate metric events received over a single // collection period. func NewAccumulation(descriptor *sdkapi.Descriptor, labels *attribute.Set, aggregator Aggregator) Accumulation { return Accumulation{ Metadata: Metadata{ descriptor: descriptor, labels: labels, }, aggregator: aggregator, } } // Aggregator returns the checkpointed aggregator. It is safe to // access the checkpointed state without locking. func (r Accumulation) Aggregator() Aggregator { return r.aggregator } // NewRecord allows Processor implementations to construct export // records. The Descriptor, Labels, and Aggregator represent // aggregate metric events received over a single collection period. func NewRecord(descriptor *sdkapi.Descriptor, labels *attribute.Set, aggregation aggregation.Aggregation, start, end time.Time) Record { return Record{ Metadata: Metadata{ descriptor: descriptor, labels: labels, }, aggregation: aggregation, start: start, end: end, } } // Aggregation returns the aggregation, an interface to the record and // its aggregator, dependent on the kind of both the input and exporter. func (r Record) Aggregation() aggregation.Aggregation { return r.aggregation } // StartTime is the start time of the interval covered by this aggregation. func (r Record) StartTime() time.Time { return r.start } // EndTime is the end time of the interval covered by this aggregation. func (r Record) EndTime() time.Time { return r.end } golang-opentelemetry-otel-1.1.0/sdk/export/metric/metric_test.go000066400000000000000000000035571414226744000250670ustar00rootroot00000000000000// 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 metric import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ) var testSlice = []attribute.KeyValue{ attribute.String("bar", "baz"), attribute.Int("foo", 42), } func newIter(slice []attribute.KeyValue) attribute.Iterator { labels := attribute.NewSet(slice...) return labels.Iter() } func TestLabelIterator(t *testing.T) { iter := newIter(testSlice) require.Equal(t, 2, iter.Len()) require.True(t, iter.Next()) require.Equal(t, attribute.String("bar", "baz"), iter.Label()) idx, kv := iter.IndexedLabel() require.Equal(t, 0, idx) require.Equal(t, attribute.String("bar", "baz"), kv) require.Equal(t, 2, iter.Len()) require.True(t, iter.Next()) require.Equal(t, attribute.Int("foo", 42), iter.Label()) idx, kv = iter.IndexedLabel() require.Equal(t, 1, idx) require.Equal(t, attribute.Int("foo", 42), kv) require.Equal(t, 2, iter.Len()) require.False(t, iter.Next()) require.Equal(t, 2, iter.Len()) } func TestEmptyLabelIterator(t *testing.T) { iter := newIter(nil) require.Equal(t, 0, iter.Len()) require.False(t, iter.Next()) } func TestIteratorToSlice(t *testing.T) { iter := newIter(testSlice) got := iter.ToSlice() require.Equal(t, testSlice, got) iter = newIter(nil) got = iter.ToSlice() require.Nil(t, got) } golang-opentelemetry-otel-1.1.0/sdk/go.mod000066400000000000000000000053451414226744000205150ustar00rootroot00000000000000module go.opentelemetry.io/otel/sdk go 1.15 replace go.opentelemetry.io/otel => ../ require ( github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/trace v1.1.0 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../internal/tools replace go.opentelemetry.io/otel/sdk => ./ replace go.opentelemetry.io/otel/metric => ../metric replace go.opentelemetry.io/otel/sdk/export/metric => ./export/metric replace go.opentelemetry.io/otel/sdk/metric => ./metric replace go.opentelemetry.io/otel/trace => ../trace replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../example/fib replace go.opentelemetry.io/otel/schema => ../schema golang-opentelemetry-otel-1.1.0/sdk/go.sum000066400000000000000000000031151414226744000205330ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/sdk/instrumentation/000077500000000000000000000000001414226744000226435ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/instrumentation/library.go000066400000000000000000000023751414226744000246450ustar00rootroot00000000000000// 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 instrumentation provides an instrumentation library structure to be passed to both the OpenTelemetry Tracer and Meter components. For more information see [this](https://github.com/open-telemetry/oteps/blob/main/text/0083-component.md). */ package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation" // Library represents the instrumentation library. type Library struct { // Name is the name of the instrumentation library. This should be the // Go package name of that library. Name string // Version is the version of the instrumentation library. Version string // SchemaURL of the telemetry emitted by the library. SchemaURL string } golang-opentelemetry-otel-1.1.0/sdk/internal/000077500000000000000000000000001414226744000212145ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/internal/internal.go000066400000000000000000000023531414226744000233620ustar00rootroot00000000000000// 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 "go.opentelemetry.io/otel/sdk/internal" import ( "fmt" "time" "go.opentelemetry.io/otel" ) // UserAgent is the user agent to be added to the outgoing // requests from the exporters. var UserAgent = fmt.Sprintf("opentelemetry-go/%s", otel.Version()) // MonotonicEndTime returns the end time at present // but offset from start, monotonically. // // The monotonic clock is used in subtractions hence // the duration since start added back to start gives // end as a monotonic time. // See https://golang.org/pkg/time/#hdr-Monotonic_Clocks func MonotonicEndTime(start time.Time) time.Time { return start.Add(time.Since(start)) } golang-opentelemetry-otel-1.1.0/sdk/internal/sanitize.go000066400000000000000000000024421414226744000233730ustar00rootroot00000000000000// 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 ( "strings" "unicode" ) const labelKeySizeLimit = 100 // Sanitize returns a string that is trunacated to 100 characters if it's too // long, and replaces non-alphanumeric characters to underscores. func Sanitize(s string) string { if len(s) == 0 { return s } if len(s) > labelKeySizeLimit { s = s[:labelKeySizeLimit] } s = strings.Map(sanitizeRune, s) if unicode.IsDigit(rune(s[0])) { s = "key_" + s } if s[0] == '_' { s = "key" + s } return s } // converts anything that is not a letter or digit to an underscore func sanitizeRune(r rune) rune { if unicode.IsLetter(r) || unicode.IsDigit(r) { return r } // Everything else turns into an underscore return '_' } golang-opentelemetry-otel-1.1.0/sdk/internal/sanitize_test.go000066400000000000000000000032021414226744000244250ustar00rootroot00000000000000// 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 ( "strings" "testing" ) func TestSanitize(t *testing.T) { tests := []struct { name string input string want string }{ { name: "trunacate long string", input: strings.Repeat("a", 101), want: strings.Repeat("a", 100), }, { name: "replace character", input: "test/key-1", want: "test_key_1", }, { name: "add prefix if starting with digit", input: "0123456789", want: "key_0123456789", }, { name: "add prefix if starting with _", input: "_0123456789", want: "key_0123456789", }, { name: "starts with _ after sanitization", input: "/0123456789", want: "key_0123456789", }, { name: "valid input", input: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789", want: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got, want := Sanitize(tt.input), tt.want; got != want { t.Errorf("sanitize() = %q; want %q", got, want) } }) } } golang-opentelemetry-otel-1.1.0/sdk/metric/000077500000000000000000000000001414226744000206635ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/000077500000000000000000000000001414226744000230055ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/aggregator.go000066400000000000000000000036041414226744000254610ustar00rootroot00000000000000// 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 aggregator // import "go.opentelemetry.io/otel/sdk/metric/aggregator" import ( "fmt" "math" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" ) // NewInconsistentAggregatorError formats an error describing an attempt to // Checkpoint or Merge different-type aggregators. The result can be unwrapped as // an ErrInconsistentType. func NewInconsistentAggregatorError(a1, a2 export.Aggregator) error { return fmt.Errorf("%w: %T and %T", aggregation.ErrInconsistentType, a1, a2) } // RangeTest is a common routine for testing for valid input values. // This rejects NaN values. This rejects negative values when the // metric instrument does not support negative values, including // monotonic counter metrics and absolute Histogram metrics. func RangeTest(num number.Number, descriptor *sdkapi.Descriptor) error { numberKind := descriptor.NumberKind() if numberKind == number.Float64Kind && math.IsNaN(num.AsFloat64()) { return aggregation.ErrNaNInput } switch descriptor.InstrumentKind() { case sdkapi.CounterInstrumentKind, sdkapi.CounterObserverInstrumentKind: if num.IsNegative(numberKind) { return aggregation.ErrNegativeInput } } return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/aggregator_test.go000066400000000000000000000057331414226744000265250ustar00rootroot00000000000000// 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 aggregator_test // import "go.opentelemetry.io/otel/sdk/metric/aggregator" import ( "errors" "math" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" ) func TestInconsistentAggregatorErr(t *testing.T) { err := aggregator.NewInconsistentAggregatorError(&sum.New(1)[0], &lastvalue.New(1)[0]) require.Equal( t, "inconsistent aggregator types: *sum.Aggregator and *lastvalue.Aggregator", err.Error(), ) require.True(t, errors.Is(err, aggregation.ErrInconsistentType)) } func testRangeNaN(t *testing.T, desc *sdkapi.Descriptor) { // If the descriptor uses int64 numbers, this won't register as NaN nan := number.NewFloat64Number(math.NaN()) err := aggregator.RangeTest(nan, desc) if desc.NumberKind() == number.Float64Kind { require.Equal(t, aggregation.ErrNaNInput, err) } else { require.Nil(t, err) } } func testRangeNegative(t *testing.T, desc *sdkapi.Descriptor) { var neg, pos number.Number if desc.NumberKind() == number.Float64Kind { pos = number.NewFloat64Number(+1) neg = number.NewFloat64Number(-1) } else { pos = number.NewInt64Number(+1) neg = number.NewInt64Number(-1) } posErr := aggregator.RangeTest(pos, desc) negErr := aggregator.RangeTest(neg, desc) require.Nil(t, posErr) require.Equal(t, negErr, aggregation.ErrNegativeInput) } func TestRangeTest(t *testing.T) { // Only Counters implement a range test. for _, nkind := range []number.Kind{number.Float64Kind, number.Int64Kind} { t.Run(nkind.String(), func(t *testing.T) { desc := metrictest.NewDescriptor( "name", sdkapi.CounterInstrumentKind, nkind, ) testRangeNegative(t, &desc) }) } } func TestNaNTest(t *testing.T) { for _, nkind := range []number.Kind{number.Float64Kind, number.Int64Kind} { t.Run(nkind.String(), func(t *testing.T) { for _, mkind := range []sdkapi.InstrumentKind{ sdkapi.CounterInstrumentKind, sdkapi.HistogramInstrumentKind, sdkapi.GaugeObserverInstrumentKind, } { desc := metrictest.NewDescriptor( "name", mkind, nkind, ) testRangeNaN(t, &desc) } }) } } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/aggregatortest/000077500000000000000000000000001414226744000260275ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/aggregatortest/test.go000066400000000000000000000157101414226744000273410ustar00rootroot00000000000000// 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 aggregatortest // import "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" import ( "context" "errors" "math/rand" "os" "sort" "testing" "unsafe" "github.com/stretchr/testify/require" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) const Magnitude = 1000 type Profile struct { NumberKind number.Kind Random func(sign int) number.Number } type NoopAggregator struct{} type NoopAggregation struct{} var _ export.Aggregator = NoopAggregator{} var _ aggregation.Aggregation = NoopAggregation{} func newProfiles() []Profile { rnd := rand.New(rand.NewSource(rand.Int63())) return []Profile{ { NumberKind: number.Int64Kind, Random: func(sign int) number.Number { return number.NewInt64Number(int64(sign) * int64(rnd.Intn(Magnitude+1))) }, }, { NumberKind: number.Float64Kind, Random: func(sign int) number.Number { return number.NewFloat64Number(float64(sign) * rnd.Float64() * Magnitude) }, }, } } func NewAggregatorTest(mkind sdkapi.InstrumentKind, nkind number.Kind) *sdkapi.Descriptor { desc := metrictest.NewDescriptor("test.name", mkind, nkind) return &desc } func RunProfiles(t *testing.T, f func(*testing.T, Profile)) { for _, profile := range newProfiles() { t.Run(profile.NumberKind.String(), func(t *testing.T) { f(t, profile) }) } } // TestMain ensures local struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []ottest.FieldOffset{ { Name: "Numbers.numbers", Offset: unsafe.Offsetof(Numbers{}.numbers), }, } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } type Numbers struct { // numbers has to be aligned for 64-bit atomic operations. numbers []number.Number kind number.Kind } func NewNumbers(kind number.Kind) Numbers { return Numbers{ kind: kind, } } func (n *Numbers) Append(v number.Number) { n.numbers = append(n.numbers, v) } func (n *Numbers) Sort() { sort.Sort(n) } func (n *Numbers) Less(i, j int) bool { return n.numbers[i].CompareNumber(n.kind, n.numbers[j]) < 0 } func (n *Numbers) Len() int { return len(n.numbers) } func (n *Numbers) Swap(i, j int) { n.numbers[i], n.numbers[j] = n.numbers[j], n.numbers[i] } func (n *Numbers) Sum() number.Number { var sum number.Number for _, num := range n.numbers { sum.AddNumber(n.kind, num) } return sum } func (n *Numbers) Count() uint64 { return uint64(len(n.numbers)) } func (n *Numbers) Min() number.Number { return n.numbers[0] } func (n *Numbers) Max() number.Number { return n.numbers[len(n.numbers)-1] } func (n *Numbers) Points() []number.Number { return n.numbers } // CheckedUpdate performs the same range test the SDK does on behalf of the aggregator. func CheckedUpdate(t *testing.T, agg export.Aggregator, number number.Number, descriptor *sdkapi.Descriptor) { ctx := context.Background() // Note: Aggregator tests are written assuming that the SDK // has performed the RangeTest. Therefore we skip errors that // would have been detected by the RangeTest. err := aggregator.RangeTest(number, descriptor) if err != nil { return } if err := agg.Update(ctx, number, descriptor); err != nil { t.Error("Unexpected Update failure", err) } } func CheckedMerge(t *testing.T, aggInto, aggFrom export.Aggregator, descriptor *sdkapi.Descriptor) { if err := aggInto.Merge(aggFrom, descriptor); err != nil { t.Error("Unexpected Merge failure", err) } } func (NoopAggregation) Kind() aggregation.Kind { return aggregation.Kind("Noop") } func (NoopAggregator) Aggregation() aggregation.Aggregation { return NoopAggregation{} } func (NoopAggregator) Update(context.Context, number.Number, *sdkapi.Descriptor) error { return nil } func (NoopAggregator) SynchronizedMove(export.Aggregator, *sdkapi.Descriptor) error { return nil } func (NoopAggregator) Merge(export.Aggregator, *sdkapi.Descriptor) error { return nil } func SynchronizedMoveResetTest(t *testing.T, mkind sdkapi.InstrumentKind, nf func(*sdkapi.Descriptor) export.Aggregator) { t.Run("reset on nil", func(t *testing.T) { // Ensures that SynchronizedMove(nil, descriptor) discards and // resets the aggregator. RunProfiles(t, func(t *testing.T, profile Profile) { descriptor := NewAggregatorTest( mkind, profile.NumberKind, ) agg := nf(descriptor) for i := 0; i < 10; i++ { x1 := profile.Random(+1) CheckedUpdate(t, agg, x1, descriptor) } require.NoError(t, agg.SynchronizedMove(nil, descriptor)) if count, ok := agg.(aggregation.Count); ok { c, err := count.Count() require.Equal(t, uint64(0), c) require.NoError(t, err) } if sum, ok := agg.(aggregation.Sum); ok { s, err := sum.Sum() require.Equal(t, number.Number(0), s) require.NoError(t, err) } if lv, ok := agg.(aggregation.LastValue); ok { v, _, err := lv.LastValue() require.Equal(t, number.Number(0), v) require.Error(t, err) require.True(t, errors.Is(err, aggregation.ErrNoData)) } }) }) t.Run("no reset on incorrect type", func(t *testing.T) { // Ensures that SynchronizedMove(wrong_type, descriptor) does not // reset the aggregator. RunProfiles(t, func(t *testing.T, profile Profile) { descriptor := NewAggregatorTest( mkind, profile.NumberKind, ) agg := nf(descriptor) var input number.Number const inval = 100 if profile.NumberKind == number.Int64Kind { input = number.NewInt64Number(inval) } else { input = number.NewFloat64Number(inval) } CheckedUpdate(t, agg, input, descriptor) err := agg.SynchronizedMove(NoopAggregator{}, descriptor) require.Error(t, err) require.True(t, errors.Is(err, aggregation.ErrInconsistentType)) // Test that the aggregator was not reset if count, ok := agg.(aggregation.Count); ok { c, err := count.Count() require.Equal(t, uint64(1), c) require.NoError(t, err) } if sum, ok := agg.(aggregation.Sum); ok { s, err := sum.Sum() require.Equal(t, input, s) require.NoError(t, err) } if lv, ok := agg.(aggregation.LastValue); ok { v, _, err := lv.LastValue() require.Equal(t, input, v) require.NoError(t, err) } }) }) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/exact/000077500000000000000000000000001414226744000241115ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/exact/exact.go000066400000000000000000000070661414226744000255550ustar00rootroot00000000000000// 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 exact // import "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" import ( "context" "sync" "time" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) type ( // Aggregator aggregates events that form a distribution, keeping // an array with the exact set of values. Aggregator struct { lock sync.Mutex samples []aggregation.Point } ) var _ export.Aggregator = &Aggregator{} var _ aggregation.Points = &Aggregator{} var _ aggregation.Count = &Aggregator{} // New returns cnt many new exact aggregators, which aggregate recorded // measurements by storing them in an array. This type uses a mutex // for Update() and SynchronizedMove() concurrency. func New(cnt int) []Aggregator { return make([]Aggregator, cnt) } // Aggregation returns an interface for reading the state of this aggregator. func (c *Aggregator) Aggregation() aggregation.Aggregation { return c } // Kind returns aggregation.ExactKind. func (c *Aggregator) Kind() aggregation.Kind { return aggregation.ExactKind } // Count returns the number of values in the checkpoint. func (c *Aggregator) Count() (uint64, error) { return uint64(len(c.samples)), nil } // Points returns access to the raw data set. func (c *Aggregator) Points() ([]aggregation.Point, error) { return c.samples, nil } // SynchronizedMove saves the current state to oa and resets the current state to // the empty set, taking a lock to prevent concurrent Update() calls. func (c *Aggregator) SynchronizedMove(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if oa != nil && o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.lock.Lock() defer c.lock.Unlock() if o != nil { o.samples = c.samples } c.samples = nil return nil } // Update adds the recorded measurement to the current data set. // Update takes a lock to prevent concurrent Update() and SynchronizedMove() // calls. func (c *Aggregator) Update(_ context.Context, number number.Number, desc *sdkapi.Descriptor) error { now := time.Now() c.lock.Lock() defer c.lock.Unlock() c.samples = append(c.samples, aggregation.Point{ Number: number, Time: now, }) return nil } // Merge combines two data sets into one. func (c *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.samples = combine(c.samples, o.samples) return nil } func combine(a, b []aggregation.Point) []aggregation.Point { result := make([]aggregation.Point, 0, len(a)+len(b)) for len(a) != 0 && len(b) != 0 { if a[0].Time.Before(b[0].Time) { result = append(result, a[0]) a = a[1:] } else { result = append(result, b[0]) b = b[1:] } } result = append(result, a...) result = append(result, b...) return result } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/exact/exact_test.go000066400000000000000000000230301414226744000266010ustar00rootroot00000000000000// 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 exact import ( "fmt" "math" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" ) type updateTest struct { count int } func requireNotAfter(t *testing.T, t1, t2 time.Time) { require.False(t, t1.After(t2), "expected %v ≤ %v", t1, t2) } func checkZero(t *testing.T, agg *Aggregator, desc *sdkapi.Descriptor) { count, err := agg.Count() require.NoError(t, err) require.Equal(t, uint64(0), count) pts, err := agg.Points() require.NoError(t, err) require.Equal(t, 0, len(pts)) } func new2() (_, _ *Aggregator) { alloc := New(2) return &alloc[0], &alloc[1] } func new4() (_, _, _, _ *Aggregator) { alloc := New(4) return &alloc[0], &alloc[1], &alloc[2], &alloc[3] } func sumOf(samples []aggregation.Point, k number.Kind) number.Number { var n number.Number for _, s := range samples { n.AddNumber(k, s.Number) } return n } func (ut *updateTest) run(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg, ckpt := new2() all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < ut.count; i++ { x := profile.Random(+1) all.Append(x) advance() aggregatortest.CheckedUpdate(t, agg, x, descriptor) y := profile.Random(-1) all.Append(y) advance() aggregatortest.CheckedUpdate(t, agg, y, descriptor) } err := agg.SynchronizedMove(ckpt, descriptor) require.NoError(t, err) checkZero(t, agg, descriptor) all.Sort() pts, err := ckpt.Points() require.Nil(t, err) sum := sumOf(pts, profile.NumberKind) allSum := all.Sum() require.InEpsilon(t, allSum.CoerceToFloat64(profile.NumberKind), sum.CoerceToFloat64(profile.NumberKind), 0.0000001, "Same sum") count, err := ckpt.Count() require.Nil(t, err) require.Equal(t, all.Count(), count, "Same count") } func TestExactUpdate(t *testing.T) { // Test with an odd an even number of measurements for count := 999; count <= 1000; count++ { t.Run(fmt.Sprint("Odd=", count%2 == 1), func(t *testing.T) { ut := updateTest{ count: count, } // Test integer and floating point aggregatortest.RunProfiles(t, ut.run) }) } } type mergeTest struct { count int absolute bool } func advance() { time.Sleep(time.Nanosecond) } func (mt *mergeTest) run(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg1, agg2, ckpt1, ckpt2 := new4() all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < mt.count; i++ { x1 := profile.Random(+1) all.Append(x1) advance() aggregatortest.CheckedUpdate(t, agg1, x1, descriptor) x2 := profile.Random(+1) all.Append(x2) advance() aggregatortest.CheckedUpdate(t, agg2, x2, descriptor) if !mt.absolute { y1 := profile.Random(-1) all.Append(y1) advance() aggregatortest.CheckedUpdate(t, agg1, y1, descriptor) y2 := profile.Random(-1) all.Append(y2) advance() aggregatortest.CheckedUpdate(t, agg2, y2, descriptor) } } require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor)) require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor)) checkZero(t, agg1, descriptor) checkZero(t, agg2, descriptor) aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor) pts, err := ckpt1.Points() require.Nil(t, err) received := aggregatortest.NewNumbers(profile.NumberKind) for i, s := range pts { received.Append(s.Number) if i > 0 { requireNotAfter(t, pts[i-1].Time, pts[i].Time) } } allSum := all.Sum() sum := sumOf(pts, profile.NumberKind) require.InEpsilon(t, allSum.CoerceToFloat64(profile.NumberKind), sum.CoerceToFloat64(profile.NumberKind), 0.0000001, "Same sum - absolute") count, err := ckpt1.Count() require.Nil(t, err) require.Equal(t, all.Count(), count, "Same count - absolute") require.Equal(t, all, received, "Same ordered contents") } func TestExactMerge(t *testing.T) { // Test with an odd an even number of measurements for count := 999; count <= 1000; count++ { t.Run(fmt.Sprint("Odd=", count%2 == 1), func(t *testing.T) { // Test absolute and non-absolute for _, absolute := range []bool{false, true} { t.Run(fmt.Sprint("Absolute=", absolute), func(t *testing.T) { mt := mergeTest{ count: count, absolute: absolute, } // Test integer and floating point aggregatortest.RunProfiles(t, mt.run) }) } }) } } func TestExactErrors(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg, ckpt := new2() descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) advance() aggregatortest.CheckedUpdate(t, agg, number.Number(0), descriptor) if profile.NumberKind == number.Float64Kind { advance() aggregatortest.CheckedUpdate(t, agg, number.NewFloat64Number(math.NaN()), descriptor) } require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) count, err := ckpt.Count() require.Equal(t, uint64(1), count, "NaN value was not counted") require.Nil(t, err) }) } func TestExactFloat64(t *testing.T) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, number.Float64Kind) fpsf := func(sign int) []float64 { // Check behavior of a bunch of odd floating // points except for NaN, which is invalid. return []float64{ 0, 1 / math.Inf(sign), 1, 2, 1e100, math.MaxFloat64, math.SmallestNonzeroFloat64, math.MaxFloat32, math.SmallestNonzeroFloat32, math.E, math.Pi, math.Phi, math.Sqrt2, math.SqrtE, math.SqrtPi, math.SqrtPhi, math.Ln2, math.Log2E, math.Ln10, math.Log10E, } } all := aggregatortest.NewNumbers(number.Float64Kind) agg, ckpt := new2() startTime := time.Now() for _, f := range fpsf(1) { all.Append(number.NewFloat64Number(f)) advance() aggregatortest.CheckedUpdate(t, agg, number.NewFloat64Number(f), descriptor) } for _, f := range fpsf(-1) { all.Append(number.NewFloat64Number(f)) advance() aggregatortest.CheckedUpdate(t, agg, number.NewFloat64Number(f), descriptor) } endTime := time.Now() require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) pts, err := ckpt.Points() require.Nil(t, err) allSum := all.Sum() sum := sumOf(pts, number.Float64Kind) require.InEpsilon(t, allSum.AsFloat64(), sum.AsFloat64(), 0.0000001, "Same sum") count, err := ckpt.Count() require.Equal(t, all.Count(), count, "Same count") require.Nil(t, err) po, err := ckpt.Points() require.Nil(t, err) require.Equal(t, all.Len(), len(po), "Points() must have same length of updates") for i := 0; i < len(po); i++ { require.Equal(t, all.Points()[i], po[i].Number, "Wrong point at position %d", i) if i > 0 { requireNotAfter(t, po[i-1].Time, po[i].Time) } } requireNotAfter(t, startTime, po[0].Time) requireNotAfter(t, po[len(po)-1].Time, endTime) } func TestSynchronizedMoveReset(t *testing.T) { aggregatortest.SynchronizedMoveResetTest( t, sdkapi.HistogramInstrumentKind, func(desc *sdkapi.Descriptor) export.Aggregator { return &New(1)[0] }, ) } func TestMergeBehavior(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { for _, forward := range []bool{false, true} { t.Run(fmt.Sprint("Forward=", forward), func(t *testing.T) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg1, agg2, ckpt, _ := new4() all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < 100; i++ { x1 := profile.Random(+1) all.Append(x1) advance() aggregatortest.CheckedUpdate(t, agg1, x1, descriptor) } for i := 0; i < 100; i++ { x2 := profile.Random(+1) all.Append(x2) advance() aggregatortest.CheckedUpdate(t, agg2, x2, descriptor) } if forward { aggregatortest.CheckedMerge(t, ckpt, agg1, descriptor) aggregatortest.CheckedMerge(t, ckpt, agg2, descriptor) } else { aggregatortest.CheckedMerge(t, ckpt, agg2, descriptor) aggregatortest.CheckedMerge(t, ckpt, agg1, descriptor) } pts, err := ckpt.Points() require.NoError(t, err) received := aggregatortest.NewNumbers(profile.NumberKind) for i, s := range pts { received.Append(s.Number) if i > 0 { requireNotAfter(t, pts[i-1].Time, pts[i].Time) } } allSum := all.Sum() sum := sumOf(pts, profile.NumberKind) require.InEpsilon(t, allSum.CoerceToFloat64(profile.NumberKind), sum.CoerceToFloat64(profile.NumberKind), 0.0000001, "Same sum - absolute") count, err := ckpt.Count() require.NoError(t, err) require.Equal(t, all.Count(), count, "Same count - absolute") require.Equal(t, all, received, "Same ordered contents") }) } }) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/histogram/000077500000000000000000000000001414226744000250025ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/histogram/benchmark_test.go000066400000000000000000000074531414226744000303330ustar00rootroot00000000000000// 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 histogram_test import ( "context" "math/rand" "testing" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" ) const inputRange = 1e6 func benchmarkHistogramSearchFloat64(b *testing.B, size int) { boundaries := make([]float64, size) for i := range boundaries { boundaries[i] = rand.Float64() * inputRange } values := make([]float64, b.N) for i := range values { values[i] = rand.Float64() * inputRange } desc := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, number.Float64Kind) agg := &histogram.New(1, desc, histogram.WithExplicitBoundaries(boundaries))[0] ctx := context.Background() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _ = agg.Update(ctx, number.NewFloat64Number(values[i]), desc) } } func BenchmarkHistogramSearchFloat64_1(b *testing.B) { benchmarkHistogramSearchFloat64(b, 1) } func BenchmarkHistogramSearchFloat64_8(b *testing.B) { benchmarkHistogramSearchFloat64(b, 8) } func BenchmarkHistogramSearchFloat64_16(b *testing.B) { benchmarkHistogramSearchFloat64(b, 16) } func BenchmarkHistogramSearchFloat64_32(b *testing.B) { benchmarkHistogramSearchFloat64(b, 32) } func BenchmarkHistogramSearchFloat64_64(b *testing.B) { benchmarkHistogramSearchFloat64(b, 64) } func BenchmarkHistogramSearchFloat64_128(b *testing.B) { benchmarkHistogramSearchFloat64(b, 128) } func BenchmarkHistogramSearchFloat64_256(b *testing.B) { benchmarkHistogramSearchFloat64(b, 256) } func BenchmarkHistogramSearchFloat64_512(b *testing.B) { benchmarkHistogramSearchFloat64(b, 512) } func BenchmarkHistogramSearchFloat64_1024(b *testing.B) { benchmarkHistogramSearchFloat64(b, 1024) } func benchmarkHistogramSearchInt64(b *testing.B, size int) { boundaries := make([]float64, size) for i := range boundaries { boundaries[i] = rand.Float64() * inputRange } values := make([]int64, b.N) for i := range values { values[i] = int64(rand.Float64() * inputRange) } desc := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, number.Int64Kind) agg := &histogram.New(1, desc, histogram.WithExplicitBoundaries(boundaries))[0] ctx := context.Background() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _ = agg.Update(ctx, number.NewInt64Number(values[i]), desc) } } func BenchmarkHistogramSearchInt64_1(b *testing.B) { benchmarkHistogramSearchInt64(b, 1) } func BenchmarkHistogramSearchInt64_8(b *testing.B) { benchmarkHistogramSearchInt64(b, 8) } func BenchmarkHistogramSearchInt64_16(b *testing.B) { benchmarkHistogramSearchInt64(b, 16) } func BenchmarkHistogramSearchInt64_32(b *testing.B) { benchmarkHistogramSearchInt64(b, 32) } func BenchmarkHistogramSearchInt64_64(b *testing.B) { benchmarkHistogramSearchInt64(b, 64) } func BenchmarkHistogramSearchInt64_128(b *testing.B) { benchmarkHistogramSearchInt64(b, 128) } func BenchmarkHistogramSearchInt64_256(b *testing.B) { benchmarkHistogramSearchInt64(b, 256) } func BenchmarkHistogramSearchInt64_512(b *testing.B) { benchmarkHistogramSearchInt64(b, 512) } func BenchmarkHistogramSearchInt64_1024(b *testing.B) { benchmarkHistogramSearchInt64(b, 1024) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/histogram/histogram.go000066400000000000000000000177711414226744000273430ustar00rootroot00000000000000// 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 histogram // import "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" import ( "context" "sort" "sync" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) // Note: This code uses a Mutex to govern access to the exclusive // aggregator state. This is in contrast to a lock-free approach // (as in the Go prometheus client) that was reverted here: // https://github.com/open-telemetry/opentelemetry-go/pull/669 type ( // Aggregator observe events and counts them in pre-determined buckets. // It also calculates the sum and count of all events. Aggregator struct { lock sync.Mutex boundaries []float64 kind number.Kind state *state } // config describes how the histogram is aggregated. config struct { // explicitBoundaries support arbitrary bucketing schemes. This // is the general case. explicitBoundaries []float64 } // Option configures a histogram config. Option interface { // apply sets one or more config fields. apply(*config) } // state represents the state of a histogram, consisting of // the sum and counts for all observed values and // the less than equal bucket count for the pre-determined boundaries. state struct { bucketCounts []uint64 sum number.Number count uint64 } ) // WithExplicitBoundaries sets the ExplicitBoundaries configuration option of a config. func WithExplicitBoundaries(explicitBoundaries []float64) Option { return explicitBoundariesOption{explicitBoundaries} } type explicitBoundariesOption struct { boundaries []float64 } func (o explicitBoundariesOption) apply(config *config) { config.explicitBoundaries = o.boundaries } // defaultExplicitBoundaries have been copied from prometheus.DefBuckets. // // Note we anticipate the use of a high-precision histogram sketch as // the standard histogram aggregator for OTLP export. // (https://github.com/open-telemetry/opentelemetry-specification/issues/982). var defaultFloat64ExplicitBoundaries = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} // defaultInt64ExplicitBoundaryMultiplier determines the default // integer histogram boundaries. const defaultInt64ExplicitBoundaryMultiplier = 1e6 // defaultInt64ExplicitBoundaries applies a multiplier to the default // float64 boundaries: [ 5K, 10K, 25K, ..., 2.5M, 5M, 10M ] var defaultInt64ExplicitBoundaries = func(bounds []float64) (asint []float64) { for _, f := range bounds { asint = append(asint, defaultInt64ExplicitBoundaryMultiplier*f) } return }(defaultFloat64ExplicitBoundaries) var _ export.Aggregator = &Aggregator{} var _ aggregation.Sum = &Aggregator{} var _ aggregation.Count = &Aggregator{} var _ aggregation.Histogram = &Aggregator{} // New returns a new aggregator for computing Histograms. // // A Histogram observe events and counts them in pre-defined buckets. // And also provides the total sum and count of all observations. // // Note that this aggregator maintains each value using independent // atomic operations, which introduces the possibility that // checkpoints are inconsistent. func New(cnt int, desc *sdkapi.Descriptor, opts ...Option) []Aggregator { var cfg config if desc.NumberKind() == number.Int64Kind { cfg.explicitBoundaries = defaultInt64ExplicitBoundaries } else { cfg.explicitBoundaries = defaultFloat64ExplicitBoundaries } for _, opt := range opts { opt.apply(&cfg) } aggs := make([]Aggregator, cnt) // Boundaries MUST be ordered otherwise the histogram could not // be properly computed. sortedBoundaries := make([]float64, len(cfg.explicitBoundaries)) copy(sortedBoundaries, cfg.explicitBoundaries) sort.Float64s(sortedBoundaries) for i := range aggs { aggs[i] = Aggregator{ kind: desc.NumberKind(), boundaries: sortedBoundaries, } aggs[i].state = aggs[i].newState() } return aggs } // Aggregation returns an interface for reading the state of this aggregator. func (c *Aggregator) Aggregation() aggregation.Aggregation { return c } // Kind returns aggregation.HistogramKind. func (c *Aggregator) Kind() aggregation.Kind { return aggregation.HistogramKind } // Sum returns the sum of all values in the checkpoint. func (c *Aggregator) Sum() (number.Number, error) { return c.state.sum, nil } // Count returns the number of values in the checkpoint. func (c *Aggregator) Count() (uint64, error) { return c.state.count, nil } // Histogram returns the count of events in pre-determined buckets. func (c *Aggregator) Histogram() (aggregation.Buckets, error) { return aggregation.Buckets{ Boundaries: c.boundaries, Counts: c.state.bucketCounts, }, nil } // SynchronizedMove saves the current state into oa and resets the current state to // the empty set. Since no locks are taken, there is a chance that // the independent Sum, Count and Bucket Count are not consistent with each // other. func (c *Aggregator) SynchronizedMove(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if oa != nil && o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } if o != nil { // Swap case: This is the ordinary case for a // synchronous instrument, where the SDK allocates two // Aggregators and lock contention is anticipated. // Reset the target state before swapping it under the // lock below. o.clearState() } c.lock.Lock() if o != nil { c.state, o.state = o.state, c.state } else { // No swap case: This is the ordinary case for an // asynchronous instrument, where the SDK allocates a // single Aggregator and there is no anticipated lock // contention. c.clearState() } c.lock.Unlock() return nil } func (c *Aggregator) newState() *state { return &state{ bucketCounts: make([]uint64, len(c.boundaries)+1), } } func (c *Aggregator) clearState() { for i := range c.state.bucketCounts { c.state.bucketCounts[i] = 0 } c.state.sum = 0 c.state.count = 0 } // Update adds the recorded measurement to the current data set. func (c *Aggregator) Update(_ context.Context, number number.Number, desc *sdkapi.Descriptor) error { kind := desc.NumberKind() asFloat := number.CoerceToFloat64(kind) bucketID := len(c.boundaries) for i, boundary := range c.boundaries { if asFloat < boundary { bucketID = i break } } // Note: Binary-search was compared using the benchmarks. The following // code is equivalent to the linear search above: // // bucketID := sort.Search(len(c.boundaries), func(i int) bool { // return asFloat < c.boundaries[i] // }) // // The binary search wins for very large boundary sets, but // the linear search performs better up through arrays between // 256 and 512 elements, which is a relatively large histogram, so we // continue to prefer linear search. c.lock.Lock() defer c.lock.Unlock() c.state.count++ c.state.sum.AddNumber(kind, number) c.state.bucketCounts[bucketID]++ return nil } // Merge combines two histograms that have the same buckets into a single one. func (c *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.state.sum.AddNumber(desc.NumberKind(), o.state.sum) c.state.count += o.state.count for i := 0; i < len(c.state.bucketCounts); i++ { c.state.bucketCounts[i] += o.state.bucketCounts[i] } return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/histogram/histogram_test.go000066400000000000000000000214731414226744000303740ustar00rootroot00000000000000// 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 histogram_test import ( "context" "math" "math/rand" "sort" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" ) const count = 100 type policy struct { name string absolute bool sign func() int } var ( positiveOnly = policy{ name: "absolute", absolute: true, sign: func() int { return +1 }, } negativeOnly = policy{ name: "negative", absolute: false, sign: func() int { return -1 }, } positiveAndNegative = policy{ name: "positiveAndNegative", absolute: false, sign: func() int { if rand.Uint32() > math.MaxUint32/2 { return -1 } return 1 }, } testBoundaries = []float64{500, 250, 750} ) func new2(desc *sdkapi.Descriptor, options ...histogram.Option) (_, _ *histogram.Aggregator) { alloc := histogram.New(2, desc, options...) return &alloc[0], &alloc[1] } func new4(desc *sdkapi.Descriptor, options ...histogram.Option) (_, _, _, _ *histogram.Aggregator) { alloc := histogram.New(4, desc, options...) return &alloc[0], &alloc[1], &alloc[2], &alloc[3] } func checkZero(t *testing.T, agg *histogram.Aggregator, desc *sdkapi.Descriptor) { asum, err := agg.Sum() require.Equal(t, number.Number(0), asum, "Empty checkpoint sum = 0") require.NoError(t, err) count, err := agg.Count() require.Equal(t, uint64(0), count, "Empty checkpoint count = 0") require.NoError(t, err) buckets, err := agg.Histogram() require.NoError(t, err) require.Equal(t, len(buckets.Counts), len(testBoundaries)+1, "There should be b + 1 counts, where b is the number of boundaries") for i, bCount := range buckets.Counts { require.Equal(t, uint64(0), uint64(bCount), "Bucket #%d must have 0 observed values", i) } } func TestHistogramAbsolute(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { testHistogram(t, profile, positiveOnly) }) } func TestHistogramNegativeOnly(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { testHistogram(t, profile, negativeOnly) }) } func TestHistogramPositiveAndNegative(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { testHistogram(t, profile, positiveAndNegative) }) } // Validates count, sum and buckets for a given profile and policy func testHistogram(t *testing.T, profile aggregatortest.Profile, policy policy) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg, ckpt := new2(descriptor, histogram.WithExplicitBoundaries(testBoundaries)) // This needs to repeat at least 3 times to uncover a failure to reset // for the overall sum and count fields, since the third time through // is the first time a `histogram.state` object is reused. for repeat := 0; repeat < 3; repeat++ { all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { x := profile.Random(policy.sign()) all.Append(x) aggregatortest.CheckedUpdate(t, agg, x, descriptor) } require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) checkZero(t, agg, descriptor) checkHistogram(t, all, profile, ckpt) } } func TestHistogramInitial(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg := &histogram.New(1, descriptor, histogram.WithExplicitBoundaries(testBoundaries))[0] buckets, err := agg.Histogram() require.NoError(t, err) require.Equal(t, len(buckets.Counts), len(testBoundaries)+1) require.Equal(t, len(buckets.Boundaries), len(testBoundaries)) }) } func TestHistogramMerge(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg1, agg2, ckpt1, ckpt2 := new4(descriptor, histogram.WithExplicitBoundaries(testBoundaries)) all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { x := profile.Random(+1) all.Append(x) aggregatortest.CheckedUpdate(t, agg1, x, descriptor) } for i := 0; i < count; i++ { x := profile.Random(+1) all.Append(x) aggregatortest.CheckedUpdate(t, agg2, x, descriptor) } require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor)) require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor)) aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor) checkHistogram(t, all, profile, ckpt1) }) } func TestHistogramNotSet(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg, ckpt := new2(descriptor, histogram.WithExplicitBoundaries(testBoundaries)) err := agg.SynchronizedMove(ckpt, descriptor) require.NoError(t, err) checkZero(t, agg, descriptor) checkZero(t, ckpt, descriptor) }) } // checkHistogram ensures the correct aggregated state between `all` // (test aggregator) and `agg` (code under test). func checkHistogram(t *testing.T, all aggregatortest.Numbers, profile aggregatortest.Profile, agg *histogram.Aggregator) { all.Sort() asum, err := agg.Sum() require.NoError(t, err) sum := all.Sum() require.InEpsilon(t, sum.CoerceToFloat64(profile.NumberKind), asum.CoerceToFloat64(profile.NumberKind), 0.000000001) count, err := agg.Count() require.NoError(t, err) require.Equal(t, all.Count(), count) buckets, err := agg.Histogram() require.NoError(t, err) require.Equal(t, len(buckets.Counts), len(testBoundaries)+1, "There should be b + 1 counts, where b is the number of boundaries") sortedBoundaries := make([]float64, len(testBoundaries)) copy(sortedBoundaries, testBoundaries) sort.Float64s(sortedBoundaries) require.EqualValues(t, sortedBoundaries, buckets.Boundaries) counts := make([]uint64, len(sortedBoundaries)+1) idx := 0 for _, p := range all.Points() { for idx < len(sortedBoundaries) && p.CoerceToFloat64(profile.NumberKind) >= sortedBoundaries[idx] { idx++ } counts[idx]++ } for i, v := range counts { bCount := uint64(buckets.Counts[i]) require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, buckets.Counts) } } func TestSynchronizedMoveReset(t *testing.T) { aggregatortest.SynchronizedMoveResetTest( t, sdkapi.HistogramInstrumentKind, func(desc *sdkapi.Descriptor) export.Aggregator { return &histogram.New(1, desc, histogram.WithExplicitBoundaries(testBoundaries))[0] }, ) } func TestHistogramDefaultBoundaries(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { ctx := context.Background() descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg, ckpt := new2(descriptor) bounds := []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} // len 11 values := append(bounds, 100) // len 12 expect := []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} // len 12 for _, value := range values { var num number.Number value -= .001 // Avoid exact boundaries if descriptor.NumberKind() == number.Int64Kind { value *= 1e6 num = number.NewInt64Number(int64(value)) } else { num = number.NewFloat64Number(value) } require.NoError(t, agg.Update(ctx, num, descriptor)) } bucks, err := agg.Histogram() require.NoError(t, err) // Check for proper lengths, 1 count in each bucket. require.Equal(t, len(values), len(bucks.Counts)) require.Equal(t, len(bounds), len(bucks.Boundaries)) require.EqualValues(t, expect, bucks.Counts) require.Equal(t, expect, bucks.Counts) // Move and repeat the test on `ckpt`. err = agg.SynchronizedMove(ckpt, descriptor) require.NoError(t, err) bucks, err = ckpt.Histogram() require.NoError(t, err) require.Equal(t, len(values), len(bucks.Counts)) require.Equal(t, len(bounds), len(bucks.Boundaries)) require.EqualValues(t, expect, bucks.Counts) }) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/lastvalue/000077500000000000000000000000001414226744000250055ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/lastvalue/lastvalue.go000066400000000000000000000100121414226744000273260ustar00rootroot00000000000000// 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 lastvalue // import "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" import ( "context" "sync/atomic" "time" "unsafe" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) type ( // Aggregator aggregates lastValue events. Aggregator struct { // value is an atomic pointer to *lastValueData. It is never nil. value unsafe.Pointer } // lastValueData stores the current value of a lastValue along with // a sequence number to determine the winner of a race. lastValueData struct { // value is the int64- or float64-encoded Set() data // // value needs to be aligned for 64-bit atomic operations. value number.Number // timestamp indicates when this record was submitted. // this can be used to pick a winner when multiple // records contain lastValue data for the same labels due // to races. timestamp time.Time } ) var _ export.Aggregator = &Aggregator{} var _ aggregation.LastValue = &Aggregator{} // An unset lastValue has zero timestamp and zero value. var unsetLastValue = &lastValueData{} // New returns a new lastValue aggregator. This aggregator retains the // last value and timestamp that were recorded. func New(cnt int) []Aggregator { aggs := make([]Aggregator, cnt) for i := range aggs { aggs[i] = Aggregator{ value: unsafe.Pointer(unsetLastValue), } } return aggs } // Aggregation returns an interface for reading the state of this aggregator. func (g *Aggregator) Aggregation() aggregation.Aggregation { return g } // Kind returns aggregation.LastValueKind. func (g *Aggregator) Kind() aggregation.Kind { return aggregation.LastValueKind } // LastValue returns the last-recorded lastValue value and the // corresponding timestamp. The error value aggregation.ErrNoData // will be returned if (due to a race condition) the checkpoint was // computed before the first value was set. func (g *Aggregator) LastValue() (number.Number, time.Time, error) { gd := (*lastValueData)(g.value) if gd == unsetLastValue { return 0, time.Time{}, aggregation.ErrNoData } return gd.value.AsNumber(), gd.timestamp, nil } // SynchronizedMove atomically saves the current value. func (g *Aggregator) SynchronizedMove(oa export.Aggregator, _ *sdkapi.Descriptor) error { if oa == nil { atomic.StorePointer(&g.value, unsafe.Pointer(unsetLastValue)) return nil } o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(g, oa) } o.value = atomic.SwapPointer(&g.value, unsafe.Pointer(unsetLastValue)) return nil } // Update atomically sets the current "last" value. func (g *Aggregator) Update(_ context.Context, number number.Number, desc *sdkapi.Descriptor) error { ngd := &lastValueData{ value: number, timestamp: time.Now(), } atomic.StorePointer(&g.value, unsafe.Pointer(ngd)) return nil } // Merge combines state from two aggregators. The most-recently set // value is chosen. func (g *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(g, oa) } ggd := (*lastValueData)(atomic.LoadPointer(&g.value)) ogd := (*lastValueData)(atomic.LoadPointer(&o.value)) if ggd.timestamp.After(ogd.timestamp) { return nil } g.value = unsafe.Pointer(ogd) return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/lastvalue/lastvalue_test.go000066400000000000000000000076731414226744000304100ustar00rootroot00000000000000// 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 lastvalue import ( "errors" "math/rand" "os" "testing" "time" "unsafe" "github.com/stretchr/testify/require" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" ) const count = 100 var _ export.Aggregator = &Aggregator{} // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []ottest.FieldOffset{ { Name: "lastValueData.value", Offset: unsafe.Offsetof(lastValueData{}.value), }, } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } func new2() (_, _ *Aggregator) { alloc := New(2) return &alloc[0], &alloc[1] } func new4() (_, _, _, _ *Aggregator) { alloc := New(4) return &alloc[0], &alloc[1], &alloc[2], &alloc[3] } func checkZero(t *testing.T, agg *Aggregator) { lv, ts, err := agg.LastValue() require.True(t, errors.Is(err, aggregation.ErrNoData)) require.Equal(t, time.Time{}, ts) require.Equal(t, number.Number(0), lv) } func TestLastValueUpdate(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg, ckpt := new2() record := aggregatortest.NewAggregatorTest(sdkapi.GaugeObserverInstrumentKind, profile.NumberKind) var last number.Number for i := 0; i < count; i++ { x := profile.Random(rand.Intn(1)*2 - 1) last = x aggregatortest.CheckedUpdate(t, agg, x, record) } err := agg.SynchronizedMove(ckpt, record) require.NoError(t, err) lv, _, err := ckpt.LastValue() require.Equal(t, last, lv, "Same last value - non-monotonic") require.Nil(t, err) }) } func TestLastValueMerge(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg1, agg2, ckpt1, ckpt2 := new4() descriptor := aggregatortest.NewAggregatorTest(sdkapi.GaugeObserverInstrumentKind, profile.NumberKind) first1 := profile.Random(+1) first2 := profile.Random(+1) first1.AddNumber(profile.NumberKind, first2) aggregatortest.CheckedUpdate(t, agg1, first1, descriptor) // Ensure these should not have the same timestamp. time.Sleep(time.Nanosecond) aggregatortest.CheckedUpdate(t, agg2, first2, descriptor) require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor)) require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor)) checkZero(t, agg1) checkZero(t, agg2) _, t1, err := ckpt1.LastValue() require.Nil(t, err) _, t2, err := ckpt2.LastValue() require.Nil(t, err) require.True(t, t1.Before(t2)) aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor) lv, ts, err := ckpt1.LastValue() require.Nil(t, err) require.Equal(t, t2, ts, "Merged timestamp - non-monotonic") require.Equal(t, first2, lv, "Merged value - non-monotonic") }) } func TestLastValueNotSet(t *testing.T) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.GaugeObserverInstrumentKind, number.Int64Kind) g, ckpt := new2() require.NoError(t, g.SynchronizedMove(ckpt, descriptor)) checkZero(t, g) } func TestSynchronizedMoveReset(t *testing.T) { aggregatortest.SynchronizedMoveResetTest( t, sdkapi.GaugeObserverInstrumentKind, func(desc *sdkapi.Descriptor) export.Aggregator { return &New(1)[0] }, ) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/minmaxsumcount/000077500000000000000000000000001414226744000260745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/minmaxsumcount/mmsc.go000066400000000000000000000103721414226744000273650ustar00rootroot00000000000000// 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 minmaxsumcount // import "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" import ( "context" "sync" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) type ( // Aggregator aggregates events that form a distribution, // keeping only the min, max, sum, and count. Aggregator struct { lock sync.Mutex kind number.Kind state } state struct { sum number.Number min number.Number max number.Number count uint64 } ) var _ export.Aggregator = &Aggregator{} var _ aggregation.MinMaxSumCount = &Aggregator{} // New returns a new aggregator for computing the min, max, sum, and // count. // // This type uses a mutex for Update() and SynchronizedMove() concurrency. func New(cnt int, desc *sdkapi.Descriptor) []Aggregator { kind := desc.NumberKind() aggs := make([]Aggregator, cnt) for i := range aggs { aggs[i] = Aggregator{ kind: kind, state: emptyState(kind), } } return aggs } // Aggregation returns an interface for reading the state of this aggregator. func (c *Aggregator) Aggregation() aggregation.Aggregation { return c } // Kind returns aggregation.MinMaxSumCountKind. func (c *Aggregator) Kind() aggregation.Kind { return aggregation.MinMaxSumCountKind } // Sum returns the sum of values in the checkpoint. func (c *Aggregator) Sum() (number.Number, error) { return c.sum, nil } // Count returns the number of values in the checkpoint. func (c *Aggregator) Count() (uint64, error) { return c.count, nil } // Min returns the minimum value in the checkpoint. // The error value aggregation.ErrNoData will be returned // if there were no measurements recorded during the checkpoint. func (c *Aggregator) Min() (number.Number, error) { if c.count == 0 { return 0, aggregation.ErrNoData } return c.min, nil } // Max returns the maximum value in the checkpoint. // The error value aggregation.ErrNoData will be returned // if there were no measurements recorded during the checkpoint. func (c *Aggregator) Max() (number.Number, error) { if c.count == 0 { return 0, aggregation.ErrNoData } return c.max, nil } // SynchronizedMove saves the current state into oa and resets the current state to // the empty set. func (c *Aggregator) SynchronizedMove(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if oa != nil && o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.lock.Lock() if o != nil { o.state = c.state } c.state = emptyState(c.kind) c.lock.Unlock() return nil } func emptyState(kind number.Kind) state { return state{ count: 0, sum: 0, min: kind.Maximum(), max: kind.Minimum(), } } // Update adds the recorded measurement to the current data set. func (c *Aggregator) Update(_ context.Context, number number.Number, desc *sdkapi.Descriptor) error { kind := desc.NumberKind() c.lock.Lock() defer c.lock.Unlock() c.count++ c.sum.AddNumber(kind, number) if number.CompareNumber(kind, c.min) < 0 { c.min = number } if number.CompareNumber(kind, c.max) > 0 { c.max = number } return nil } // Merge combines two data sets into one. func (c *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.count += o.count c.sum.AddNumber(desc.NumberKind(), o.sum) if c.min.CompareNumber(desc.NumberKind(), o.min) > 0 { c.min.SetNumber(o.min) } if c.max.CompareNumber(desc.NumberKind(), o.max) < 0 { c.max.SetNumber(o.max) } return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/minmaxsumcount/mmsc_test.go000066400000000000000000000143421414226744000304250ustar00rootroot00000000000000// 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 minmaxsumcount import ( "errors" "math" "math/rand" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" ) const count = 100 type policy struct { name string absolute bool sign func() int } var ( positiveOnly = policy{ name: "absolute", absolute: true, sign: func() int { return +1 }, } negativeOnly = policy{ name: "negative", absolute: false, sign: func() int { return -1 }, } positiveAndNegative = policy{ name: "positiveAndNegative", absolute: false, sign: func() int { if rand.Uint32() > math.MaxUint32/2 { return -1 } return 1 }, } ) func TestMinMaxSumCountAbsolute(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { minMaxSumCount(t, profile, positiveOnly) }) } func TestMinMaxSumCountNegativeOnly(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { minMaxSumCount(t, profile, negativeOnly) }) } func TestMinMaxSumCountPositiveAndNegative(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { minMaxSumCount(t, profile, positiveAndNegative) }) } func new2(desc *sdkapi.Descriptor) (_, _ *Aggregator) { alloc := New(2, desc) return &alloc[0], &alloc[1] } func new4(desc *sdkapi.Descriptor) (_, _, _, _ *Aggregator) { alloc := New(4, desc) return &alloc[0], &alloc[1], &alloc[2], &alloc[3] } func checkZero(t *testing.T, agg *Aggregator, desc *sdkapi.Descriptor) { kind := desc.NumberKind() sum, err := agg.Sum() require.NoError(t, err) require.Equal(t, kind.Zero(), sum) count, err := agg.Count() require.NoError(t, err) require.Equal(t, uint64(0), count) max, err := agg.Max() require.True(t, errors.Is(err, aggregation.ErrNoData)) require.Equal(t, kind.Zero(), max) min, err := agg.Min() require.True(t, errors.Is(err, aggregation.ErrNoData)) require.Equal(t, kind.Zero(), min) } // Validates min, max, sum and count for a given profile and policy func minMaxSumCount(t *testing.T, profile aggregatortest.Profile, policy policy) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg, ckpt := new2(descriptor) all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { x := profile.Random(policy.sign()) all.Append(x) aggregatortest.CheckedUpdate(t, agg, x, descriptor) } require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) checkZero(t, agg, descriptor) all.Sort() aggSum, err := ckpt.Sum() require.Nil(t, err) allSum := all.Sum() require.InEpsilon(t, (&allSum).CoerceToFloat64(profile.NumberKind), aggSum.CoerceToFloat64(profile.NumberKind), 0.000000001, "Same sum - "+policy.name) count, err := ckpt.Count() require.Equal(t, all.Count(), count, "Same count -"+policy.name) require.Nil(t, err) min, err := ckpt.Min() require.Nil(t, err) require.Equal(t, all.Min(), min, "Same min -"+policy.name) max, err := ckpt.Max() require.Nil(t, err) require.Equal(t, all.Max(), max, "Same max -"+policy.name) } func TestMinMaxSumCountMerge(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) agg1, agg2, ckpt1, ckpt2 := new4(descriptor) all := aggregatortest.NewNumbers(profile.NumberKind) for i := 0; i < count; i++ { x := profile.Random(+1) all.Append(x) aggregatortest.CheckedUpdate(t, agg1, x, descriptor) } for i := 0; i < count; i++ { x := profile.Random(+1) all.Append(x) aggregatortest.CheckedUpdate(t, agg2, x, descriptor) } require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor)) require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor)) checkZero(t, agg1, descriptor) checkZero(t, agg2, descriptor) aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor) all.Sort() aggSum, err := ckpt1.Sum() require.Nil(t, err) allSum := all.Sum() require.InEpsilon(t, (&allSum).CoerceToFloat64(profile.NumberKind), aggSum.CoerceToFloat64(profile.NumberKind), 0.000000001, "Same sum - absolute") count, err := ckpt1.Count() require.Equal(t, all.Count(), count, "Same count - absolute") require.Nil(t, err) min, err := ckpt1.Min() require.Nil(t, err) require.Equal(t, all.Min(), min, "Same min - absolute") max, err := ckpt1.Max() require.Nil(t, err) require.Equal(t, all.Max(), max, "Same max - absolute") }) } func TestMaxSumCountNotSet(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) alloc := New(2, descriptor) agg, ckpt := &alloc[0], &alloc[1] require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) asum, err := ckpt.Sum() require.Equal(t, number.Number(0), asum, "Empty checkpoint sum = 0") require.Nil(t, err) count, err := ckpt.Count() require.Equal(t, uint64(0), count, "Empty checkpoint count = 0") require.Nil(t, err) max, err := ckpt.Max() require.Equal(t, aggregation.ErrNoData, err) require.Equal(t, number.Number(0), max) }) } func TestSynchronizedMoveReset(t *testing.T) { aggregatortest.SynchronizedMoveResetTest( t, sdkapi.HistogramInstrumentKind, func(desc *sdkapi.Descriptor) export.Aggregator { return &New(1, desc)[0] }, ) } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/sum/000077500000000000000000000000001414226744000236115ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/sum/sum.go000066400000000000000000000063611414226744000247520ustar00rootroot00000000000000// 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 sum // import "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" import ( "context" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) // Aggregator aggregates counter events. type Aggregator struct { // current holds current increments to this counter record // current needs to be aligned for 64-bit atomic operations. value number.Number } var _ export.Aggregator = &Aggregator{} var _ export.Subtractor = &Aggregator{} var _ aggregation.Sum = &Aggregator{} // New returns a new counter aggregator implemented by atomic // operations. This aggregator implements the aggregation.Sum // export interface. func New(cnt int) []Aggregator { return make([]Aggregator, cnt) } // Aggregation returns an interface for reading the state of this aggregator. func (c *Aggregator) Aggregation() aggregation.Aggregation { return c } // Kind returns aggregation.SumKind. func (c *Aggregator) Kind() aggregation.Kind { return aggregation.SumKind } // Sum returns the last-checkpointed sum. This will never return an // error. func (c *Aggregator) Sum() (number.Number, error) { return c.value, nil } // SynchronizedMove atomically saves the current value into oa and resets the // current sum to zero. func (c *Aggregator) SynchronizedMove(oa export.Aggregator, _ *sdkapi.Descriptor) error { if oa == nil { c.value.SetRawAtomic(0) return nil } o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } o.value = c.value.SwapNumberAtomic(number.Number(0)) return nil } // Update atomically adds to the current value. func (c *Aggregator) Update(_ context.Context, num number.Number, desc *sdkapi.Descriptor) error { c.value.AddNumberAtomic(desc.NumberKind(), num) return nil } // Merge combines two counters by adding their sums. func (c *Aggregator) Merge(oa export.Aggregator, desc *sdkapi.Descriptor) error { o, _ := oa.(*Aggregator) if o == nil { return aggregator.NewInconsistentAggregatorError(c, oa) } c.value.AddNumber(desc.NumberKind(), o.value) return nil } func (c *Aggregator) Subtract(opAgg, resAgg export.Aggregator, descriptor *sdkapi.Descriptor) error { op, _ := opAgg.(*Aggregator) if op == nil { return aggregator.NewInconsistentAggregatorError(c, opAgg) } res, _ := resAgg.(*Aggregator) if res == nil { return aggregator.NewInconsistentAggregatorError(c, resAgg) } res.value = c.value res.value.AddNumber(descriptor.NumberKind(), number.NewNumberSignChange(descriptor.NumberKind(), op.value)) return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/aggregator/sum/sum_test.go000066400000000000000000000101171414226744000260030ustar00rootroot00000000000000// 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 sum import ( "os" "testing" "unsafe" "github.com/stretchr/testify/require" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" ) const count = 100 // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { fields := []ottest.FieldOffset{ { Name: "Aggregator.value", Offset: unsafe.Offsetof(Aggregator{}.value), }, } if !ottest.Aligned8Byte(fields, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } func new2() (_, _ *Aggregator) { alloc := New(2) return &alloc[0], &alloc[1] } func new4() (_, _, _, _ *Aggregator) { alloc := New(4) return &alloc[0], &alloc[1], &alloc[2], &alloc[3] } func checkZero(t *testing.T, agg *Aggregator, desc *sdkapi.Descriptor) { kind := desc.NumberKind() sum, err := agg.Sum() require.NoError(t, err) require.Equal(t, kind.Zero(), sum) } func TestCounterSum(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg, ckpt := new2() descriptor := aggregatortest.NewAggregatorTest(sdkapi.CounterInstrumentKind, profile.NumberKind) sum := number.Number(0) for i := 0; i < count; i++ { x := profile.Random(+1) sum.AddNumber(profile.NumberKind, x) aggregatortest.CheckedUpdate(t, agg, x, descriptor) } err := agg.SynchronizedMove(ckpt, descriptor) require.NoError(t, err) checkZero(t, agg, descriptor) asum, err := ckpt.Sum() require.Equal(t, sum, asum, "Same sum - monotonic") require.Nil(t, err) }) } func TestHistogramSum(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg, ckpt := new2() descriptor := aggregatortest.NewAggregatorTest(sdkapi.HistogramInstrumentKind, profile.NumberKind) sum := number.Number(0) for i := 0; i < count; i++ { r1 := profile.Random(+1) r2 := profile.Random(-1) aggregatortest.CheckedUpdate(t, agg, r1, descriptor) aggregatortest.CheckedUpdate(t, agg, r2, descriptor) sum.AddNumber(profile.NumberKind, r1) sum.AddNumber(profile.NumberKind, r2) } require.NoError(t, agg.SynchronizedMove(ckpt, descriptor)) checkZero(t, agg, descriptor) asum, err := ckpt.Sum() require.Equal(t, sum, asum, "Same sum - monotonic") require.Nil(t, err) }) } func TestCounterMerge(t *testing.T) { aggregatortest.RunProfiles(t, func(t *testing.T, profile aggregatortest.Profile) { agg1, agg2, ckpt1, ckpt2 := new4() descriptor := aggregatortest.NewAggregatorTest(sdkapi.CounterInstrumentKind, profile.NumberKind) sum := number.Number(0) for i := 0; i < count; i++ { x := profile.Random(+1) sum.AddNumber(profile.NumberKind, x) aggregatortest.CheckedUpdate(t, agg1, x, descriptor) aggregatortest.CheckedUpdate(t, agg2, x, descriptor) } require.NoError(t, agg1.SynchronizedMove(ckpt1, descriptor)) require.NoError(t, agg2.SynchronizedMove(ckpt2, descriptor)) checkZero(t, agg1, descriptor) checkZero(t, agg2, descriptor) aggregatortest.CheckedMerge(t, ckpt1, ckpt2, descriptor) sum.AddNumber(descriptor.NumberKind(), sum) asum, err := ckpt1.Sum() require.Equal(t, sum, asum, "Same sum - monotonic") require.Nil(t, err) }) } func TestSynchronizedMoveReset(t *testing.T) { aggregatortest.SynchronizedMoveResetTest( t, sdkapi.CounterObserverInstrumentKind, func(desc *sdkapi.Descriptor) export.Aggregator { return &New(1)[0] }, ) } golang-opentelemetry-otel-1.1.0/sdk/metric/alignment_test.go000066400000000000000000000020161414226744000242260ustar00rootroot00000000000000// 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 metric import ( "os" "testing" ottest "go.opentelemetry.io/otel/internal/internaltest" ) // Ensure struct alignment prior to running tests. func TestMain(m *testing.M) { offsets := AtomicFieldOffsets() var r []ottest.FieldOffset for name, offset := range offsets { r = append(r, ottest.FieldOffset{ Name: name, Offset: offset, }) } if !ottest.Aligned8Byte(r, os.Stderr) { os.Exit(1) } os.Exit(m.Run()) } golang-opentelemetry-otel-1.1.0/sdk/metric/atomicfields.go000066400000000000000000000016011414226744000236530ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/sdk/metric" import "unsafe" func AtomicFieldOffsets() map[string]uintptr { return map[string]uintptr{ "record.refMapped.value": unsafe.Offsetof(record{}.refMapped.value), "record.updateCount": unsafe.Offsetof(record{}.updateCount), } } golang-opentelemetry-otel-1.1.0/sdk/metric/benchmark_test.go000066400000000000000000000267301414226744000242130ustar00rootroot00000000000000// 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 metric_test import ( "context" "fmt" "math/rand" "testing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" sdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" ) type benchFixture struct { meter metric.Meter accumulator *sdk.Accumulator B *testing.B export.AggregatorSelector } func newFixture(b *testing.B) *benchFixture { b.ReportAllocs() bf := &benchFixture{ B: b, AggregatorSelector: processortest.AggregatorSelector(), } bf.accumulator = sdk.NewAccumulator(bf) bf.meter = metric.WrapMeterImpl(bf.accumulator) return bf } func (f *benchFixture) Process(export.Accumulation) error { return nil } func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { return f.meter } func (f *benchFixture) meterMust() metric.MeterMust { return metric.Must(f.meter) } func makeManyLabels(n int) [][]attribute.KeyValue { r := make([][]attribute.KeyValue, n) for i := 0; i < n; i++ { r[i] = makeLabels(1) } return r } func makeLabels(n int) []attribute.KeyValue { used := map[string]bool{} l := make([]attribute.KeyValue, n) for i := 0; i < n; i++ { var k string for { k = fmt.Sprint("k", rand.Intn(1000000000)) if !used[k] { used[k] = true break } } l[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000))) } return l } func benchmarkLabels(b *testing.B, n int) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(n) cnt := fix.meterMust().NewInt64Counter("int64.sum") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Add(ctx, 1, labs...) } } func BenchmarkInt64CounterAddWithLabels_1(b *testing.B) { benchmarkLabels(b, 1) } func BenchmarkInt64CounterAddWithLabels_2(b *testing.B) { benchmarkLabels(b, 2) } func BenchmarkInt64CounterAddWithLabels_4(b *testing.B) { benchmarkLabels(b, 4) } func BenchmarkInt64CounterAddWithLabels_8(b *testing.B) { benchmarkLabels(b, 8) } func BenchmarkInt64CounterAddWithLabels_16(b *testing.B) { benchmarkLabels(b, 16) } // Note: performance does not depend on label set size for the // benchmarks below--all are benchmarked for a single attribute. func BenchmarkAcquireNewHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) cnt := fix.meterMust().NewInt64Counter("int64.sum") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...) } } func BenchmarkAcquireExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) cnt := fix.meterMust().NewInt64Counter("int64.sum") for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...).Unbind() } b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...) } } func BenchmarkAcquireReleaseExistingHandle(b *testing.B) { fix := newFixture(b) labelSets := makeManyLabels(b.N) cnt := fix.meterMust().NewInt64Counter("int64.sum") for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...).Unbind() } b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Bind(labelSets[i]...).Unbind() } } // Iterators var benchmarkIteratorVar attribute.KeyValue func benchmarkIterator(b *testing.B, n int) { labels := attribute.NewSet(makeLabels(n)...) b.ResetTimer() for i := 0; i < b.N; i++ { iter := labels.Iter() for iter.Next() { benchmarkIteratorVar = iter.Label() } } } func BenchmarkIterator_0(b *testing.B) { benchmarkIterator(b, 0) } func BenchmarkIterator_1(b *testing.B) { benchmarkIterator(b, 1) } func BenchmarkIterator_2(b *testing.B) { benchmarkIterator(b, 2) } func BenchmarkIterator_4(b *testing.B) { benchmarkIterator(b, 4) } func BenchmarkIterator_8(b *testing.B) { benchmarkIterator(b, 8) } func BenchmarkIterator_16(b *testing.B) { benchmarkIterator(b, 16) } // Counters func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { // Compare with BenchmarkInt64CounterAdd() to see overhead of global // package. This is in the SDK to avoid the API from depending on the // SDK. ctx := context.Background() fix := newFixture(b) sdk := global.Meter("test") global.SetMeterProvider(fix) labs := []attribute.KeyValue{attribute.String("A", "B")} cnt := Must(sdk).NewInt64Counter("int64.sum") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Add(ctx, 1, labs...) } } func BenchmarkInt64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) cnt := fix.meterMust().NewInt64Counter("int64.sum") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Add(ctx, 1, labs...) } } func BenchmarkInt64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) cnt := fix.meterMust().NewInt64Counter("int64.sum") handle := cnt.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Add(ctx, 1) } } func BenchmarkFloat64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) cnt := fix.meterMust().NewFloat64Counter("float64.sum") b.ResetTimer() for i := 0; i < b.N; i++ { cnt.Add(ctx, 1.1, labs...) } } func BenchmarkFloat64CounterHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) cnt := fix.meterMust().NewFloat64Counter("float64.sum") handle := cnt.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Add(ctx, 1.1) } } // LastValue func BenchmarkInt64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewInt64Histogram("int64.lastvalue") b.ResetTimer() for i := 0; i < b.N; i++ { mea.Record(ctx, int64(i), labs...) } } func BenchmarkInt64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewInt64Histogram("int64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Record(ctx, int64(i)) } } func BenchmarkFloat64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewFloat64Histogram("float64.lastvalue") b.ResetTimer() for i := 0; i < b.N; i++ { mea.Record(ctx, float64(i), labs...) } } func BenchmarkFloat64LastValueHandleAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewFloat64Histogram("float64.lastvalue") handle := mea.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Record(ctx, float64(i)) } } // Histograms func benchmarkInt64HistogramAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewInt64Histogram(name) b.ResetTimer() for i := 0; i < b.N; i++ { mea.Record(ctx, int64(i), labs...) } } func benchmarkInt64HistogramHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewInt64Histogram(name) handle := mea.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Record(ctx, int64(i)) } } func benchmarkFloat64HistogramAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewFloat64Histogram(name) b.ResetTimer() for i := 0; i < b.N; i++ { mea.Record(ctx, float64(i), labs...) } } func benchmarkFloat64HistogramHandleAdd(b *testing.B, name string) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) mea := fix.meterMust().NewFloat64Histogram(name) handle := mea.Bind(labs...) b.ResetTimer() for i := 0; i < b.N; i++ { handle.Record(ctx, float64(i)) } } // Observers func BenchmarkObserverRegistration(b *testing.B) { fix := newFixture(b) names := make([]string, 0, b.N) for i := 0; i < b.N; i++ { names = append(names, fmt.Sprintf("test.%d.lastvalue", i)) } cb := func(_ context.Context, result metric.Int64ObserverResult) {} b.ResetTimer() for i := 0; i < b.N; i++ { fix.meterMust().NewInt64GaugeObserver(names[i], cb) } } func BenchmarkGaugeObserverObservationInt64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) _ = fix.meterMust().NewInt64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((int64)(i), labs...) } }) b.ResetTimer() fix.accumulator.Collect(ctx) } func BenchmarkGaugeObserverObservationFloat64(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) _ = fix.meterMust().NewFloat64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) { for i := 0; i < b.N; i++ { result.Observe((float64)(i), labs...) } }) b.ResetTimer() fix.accumulator.Collect(ctx) } // MaxSumCount func BenchmarkInt64MaxSumCountAdd(b *testing.B) { benchmarkInt64HistogramAdd(b, "int64.minmaxsumcount") } func BenchmarkInt64MaxSumCountHandleAdd(b *testing.B) { benchmarkInt64HistogramHandleAdd(b, "int64.minmaxsumcount") } func BenchmarkFloat64MaxSumCountAdd(b *testing.B) { benchmarkFloat64HistogramAdd(b, "float64.minmaxsumcount") } func BenchmarkFloat64MaxSumCountHandleAdd(b *testing.B) { benchmarkFloat64HistogramHandleAdd(b, "float64.minmaxsumcount") } // Exact func BenchmarkInt64ExactAdd(b *testing.B) { benchmarkInt64HistogramAdd(b, "int64.exact") } func BenchmarkInt64ExactHandleAdd(b *testing.B) { benchmarkInt64HistogramHandleAdd(b, "int64.exact") } func BenchmarkFloat64ExactAdd(b *testing.B) { benchmarkFloat64HistogramAdd(b, "float64.exact") } func BenchmarkFloat64ExactHandleAdd(b *testing.B) { benchmarkFloat64HistogramHandleAdd(b, "float64.exact") } // BatchRecord func benchmarkBatchRecord8Labels(b *testing.B, numInst int) { const numLabels = 8 ctx := context.Background() fix := newFixture(b) labs := makeLabels(numLabels) var meas []sdkapi.Measurement for i := 0; i < numInst; i++ { inst := fix.meterMust().NewInt64Counter(fmt.Sprintf("int64.%d.sum", i)) meas = append(meas, inst.Measurement(1)) } b.ResetTimer() for i := 0; i < b.N; i++ { fix.accumulator.RecordBatch(ctx, labs, meas...) } } func BenchmarkBatchRecord8Labels_1Instrument(b *testing.B) { benchmarkBatchRecord8Labels(b, 1) } func BenchmarkBatchRecord_8Labels_2Instruments(b *testing.B) { benchmarkBatchRecord8Labels(b, 2) } func BenchmarkBatchRecord_8Labels_4Instruments(b *testing.B) { benchmarkBatchRecord8Labels(b, 4) } func BenchmarkBatchRecord_8Labels_8Instruments(b *testing.B) { benchmarkBatchRecord8Labels(b, 8) } // Record creation func BenchmarkRepeatedDirectCalls(b *testing.B) { ctx := context.Background() fix := newFixture(b) c := fix.meterMust().NewInt64Counter("int64.sum") k := attribute.String("bench", "true") b.ResetTimer() for i := 0; i < b.N; i++ { c.Add(ctx, 1, k) fix.accumulator.Collect(ctx) } } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/000077500000000000000000000000001414226744000230465ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/000077500000000000000000000000001414226744000241275ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/config.go000066400000000000000000000074261414226744000257340ustar00rootroot00000000000000// 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 basic // import "go.opentelemetry.io/otel/sdk/metric/controller/basic" import ( "time" "go.opentelemetry.io/otel" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/resource" ) // config contains configuration for a basic Controller. type config struct { // Resource is the OpenTelemetry resource associated with all Meters // created by the Controller. Resource *resource.Resource // CollectPeriod is the interval between calls to Collect a // checkpoint. // // When pulling metrics and not exporting, this is the minimum // time between calls to Collect. In a pull-only // configuration, collection is performed on demand; set // CollectPeriod to 0 always recompute the export record set. // // When exporting metrics, this must be > 0. // // Default value is 10s. CollectPeriod time.Duration // CollectTimeout is the timeout of the Context passed to // Collect() and subsequently to Observer instrument callbacks. // // Default value is 10s. If zero, no Collect timeout is applied. CollectTimeout time.Duration // Exporter is used for exporting metric data. // // Note: Exporters such as Prometheus that pull data do not implement // export.Exporter. These will directly call Collect() and ForEach(). Exporter export.Exporter // PushTimeout is the timeout of the Context when a exporter is configured. // // Default value is 10s. If zero, no Export timeout is applied. PushTimeout time.Duration } // Option is the interface that applies the value to a configuration option. type Option interface { // apply sets the Option value of a Config. apply(*config) } // WithResource sets the Resource configuration option of a Config by merging it // with the Resource configuration in the environment. func WithResource(r *resource.Resource) Option { return resourceOption{r} } type resourceOption struct{ *resource.Resource } func (o resourceOption) apply(cfg *config) { res, err := resource.Merge(cfg.Resource, o.Resource) if err != nil { otel.Handle(err) } cfg.Resource = res } // WithCollectPeriod sets the CollectPeriod configuration option of a Config. func WithCollectPeriod(period time.Duration) Option { return collectPeriodOption(period) } type collectPeriodOption time.Duration func (o collectPeriodOption) apply(cfg *config) { cfg.CollectPeriod = time.Duration(o) } // WithCollectTimeout sets the CollectTimeout configuration option of a Config. func WithCollectTimeout(timeout time.Duration) Option { return collectTimeoutOption(timeout) } type collectTimeoutOption time.Duration func (o collectTimeoutOption) apply(cfg *config) { cfg.CollectTimeout = time.Duration(o) } // WithExporter sets the exporter configuration option of a Config. func WithExporter(exporter export.Exporter) Option { return exporterOption{exporter} } type exporterOption struct{ exporter export.Exporter } func (o exporterOption) apply(cfg *config) { cfg.Exporter = o.exporter } // WithPushTimeout sets the PushTimeout configuration option of a Config. func WithPushTimeout(timeout time.Duration) Option { return pushTimeoutOption(timeout) } type pushTimeoutOption time.Duration func (o pushTimeoutOption) apply(cfg *config) { cfg.PushTimeout = time.Duration(o) } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/config_test.go000066400000000000000000000021311414226744000267570ustar00rootroot00000000000000// 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 basic import ( "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" ) func TestWithResource(t *testing.T) { r := resource.NewSchemaless(attribute.String("A", "a")) c := &config{} WithResource(r).apply(c) assert.Equal(t, r.Equivalent(), c.Resource.Equivalent()) // Ensure overwriting works. c = &config{Resource: &resource.Resource{}} WithResource(r).apply(c) assert.Equal(t, r.Equivalent(), c.Resource.Equivalent()) } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/controller.go000066400000000000000000000257501414226744000266520ustar00rootroot00000000000000// 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 basic // import "go.opentelemetry.io/otel/sdk/metric/controller/basic" import ( "context" "fmt" "sync" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/metric/registry" "go.opentelemetry.io/otel/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/instrumentation" sdk "go.opentelemetry.io/otel/sdk/metric" controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" "go.opentelemetry.io/otel/sdk/resource" ) // DefaultPeriod is used for: // // - the minimum time between calls to Collect() // - the timeout for Export() // - the timeout for Collect(). const DefaultPeriod = 10 * time.Second // ErrControllerStarted indicates that a controller was started more // than once. var ErrControllerStarted = fmt.Errorf("controller already started") // Controller organizes and synchronizes collection of metric data in // both "pull" and "push" configurations. This supports two distinct // modes: // // - Push and Pull: Start() must be called to begin calling the exporter; // Collect() is called periodically by a background thread after starting // the controller. // - Pull-Only: Start() is optional in this case, to call Collect periodically. // If Start() is not called, Collect() can be called manually to initiate // collection // // The controller supports mixing push and pull access to metric data // using the export.Reader RWLock interface. Collection will // be blocked by a pull request in the basic controller. type Controller struct { // lock protects libraries and synchronizes Start() and Stop(). lock sync.Mutex // TODO: libraries is synchronized by lock, but could be // accomplished using a sync.Map. The SDK specification will // probably require this, as the draft already states that // Stop() and MeterProvider.Meter() should not block each // other. libraries map[instrumentation.Library]*registry.UniqueInstrumentMeterImpl checkpointerFactory export.CheckpointerFactory resource *resource.Resource exporter export.Exporter wg sync.WaitGroup stopCh chan struct{} clock controllerTime.Clock ticker controllerTime.Ticker collectPeriod time.Duration collectTimeout time.Duration pushTimeout time.Duration // collectedTime is used only in configurations with no // exporter, when ticker != nil. collectedTime time.Time } var _ export.InstrumentationLibraryReader = &Controller{} var _ metric.MeterProvider = &Controller{} func (c *Controller) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { cfg := metric.NewMeterConfig(opts...) library := instrumentation.Library{ Name: instrumentationName, Version: cfg.InstrumentationVersion(), SchemaURL: cfg.SchemaURL(), } c.lock.Lock() defer c.lock.Unlock() m, ok := c.libraries[library] if !ok { checkpointer := c.checkpointerFactory.NewCheckpointer() accumulator := sdk.NewAccumulator(checkpointer) m = registry.NewUniqueInstrumentMeterImpl(&accumulatorCheckpointer{ Accumulator: accumulator, checkpointer: checkpointer, library: library, }) c.libraries[library] = m } return metric.WrapMeterImpl(m) } type accumulatorCheckpointer struct { *sdk.Accumulator checkpointer export.Checkpointer library instrumentation.Library } // New constructs a Controller using the provided checkpointer factory // and options (including optional exporter) to configure a metric // export pipeline. func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller { c := &config{ CollectPeriod: DefaultPeriod, CollectTimeout: DefaultPeriod, PushTimeout: DefaultPeriod, } for _, opt := range opts { opt.apply(c) } if c.Resource == nil { c.Resource = resource.Default() } else { var err error c.Resource, err = resource.Merge(resource.Environment(), c.Resource) if err != nil { otel.Handle(err) } } return &Controller{ libraries: map[instrumentation.Library]*registry.UniqueInstrumentMeterImpl{}, checkpointerFactory: checkpointerFactory, exporter: c.Exporter, resource: c.Resource, stopCh: nil, clock: controllerTime.RealClock{}, collectPeriod: c.CollectPeriod, collectTimeout: c.CollectTimeout, pushTimeout: c.PushTimeout, } } // SetClock supports setting a mock clock for testing. This must be // called before Start(). func (c *Controller) SetClock(clock controllerTime.Clock) { c.lock.Lock() defer c.lock.Unlock() c.clock = clock } // Resource returns the *resource.Resource associated with this // controller. func (c *Controller) Resource() *resource.Resource { return c.resource } // Start begins a ticker that periodically collects and exports // metrics with the configured interval. This is required for calling // a configured Exporter (see WithExporter) and is otherwise optional // when only pulling metric data. // // The passed context is passed to Collect() and subsequently to // asynchronous instrument callbacks. Returns an error when the // controller was already started. // // Note that it is not necessary to Start a controller when only // pulling data; use the Collect() and ForEach() methods directly in // this case. func (c *Controller) Start(ctx context.Context) error { c.lock.Lock() defer c.lock.Unlock() if c.stopCh != nil { return ErrControllerStarted } c.wg.Add(1) c.stopCh = make(chan struct{}) c.ticker = c.clock.Ticker(c.collectPeriod) go c.runTicker(ctx, c.stopCh) return nil } // Stop waits for the background goroutine to return and then collects // and exports metrics one last time before returning. The passed // context is passed to the final Collect() and subsequently to the // final asynchronous instruments. // // Note that Stop() will not cancel an ongoing collection or export. func (c *Controller) Stop(ctx context.Context) error { if lastCollection := func() bool { c.lock.Lock() defer c.lock.Unlock() if c.stopCh == nil { return false } close(c.stopCh) c.stopCh = nil c.wg.Wait() c.ticker.Stop() c.ticker = nil return true }(); !lastCollection { return nil } return c.collect(ctx) } // runTicker collection on ticker events until the stop channel is closed. func (c *Controller) runTicker(ctx context.Context, stopCh chan struct{}) { defer c.wg.Done() for { select { case <-stopCh: return case <-c.ticker.C(): if err := c.collect(ctx); err != nil { otel.Handle(err) } } } } // collect computes a checkpoint and optionally exports it. func (c *Controller) collect(ctx context.Context) error { if err := c.checkpoint(ctx); err != nil { return err } if c.exporter == nil { return nil } // Note: this is not subject to collectTimeout. This blocks the next // collection despite collectTimeout because it holds a lock. return c.export(ctx) } // accumulatorList returns a snapshot of current accumulators // registered to this controller. This briefly locks the controller. func (c *Controller) accumulatorList() []*accumulatorCheckpointer { c.lock.Lock() defer c.lock.Unlock() var r []*accumulatorCheckpointer for _, entry := range c.libraries { acc, ok := entry.MeterImpl().(*accumulatorCheckpointer) if ok { r = append(r, acc) } } return r } // checkpoint calls the Accumulator and Checkpointer interfaces to // compute the Reader. This applies the configured collection // timeout. Note that this does not try to cancel a Collect or Export // when Stop() is called. func (c *Controller) checkpoint(ctx context.Context) error { for _, impl := range c.accumulatorList() { if err := c.checkpointSingleAccumulator(ctx, impl); err != nil { return err } } return nil } // checkpointSingleAccumulator checkpoints a single instrumentation // library's accumulator, which involves calling // checkpointer.StartCollection, accumulator.Collect, and // checkpointer.FinishCollection in sequence. func (c *Controller) checkpointSingleAccumulator(ctx context.Context, ac *accumulatorCheckpointer) error { ckpt := ac.checkpointer.Reader() ckpt.Lock() defer ckpt.Unlock() ac.checkpointer.StartCollection() if c.collectTimeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, c.collectTimeout) defer cancel() } _ = ac.Accumulator.Collect(ctx) var err error select { case <-ctx.Done(): err = ctx.Err() default: // The context wasn't done, ok. } // Finish the checkpoint whether the accumulator timed out or not. if cerr := ac.checkpointer.FinishCollection(); cerr != nil { if err == nil { err = cerr } else { err = fmt.Errorf("%s: %w", cerr.Error(), err) } } return err } // export calls the exporter with a read lock on the Reader, // applying the configured export timeout. func (c *Controller) export(ctx context.Context) error { if c.pushTimeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, c.pushTimeout) defer cancel() } return c.exporter.Export(ctx, c.resource, c) } // ForEach implements export.InstrumentationLibraryReader. func (c *Controller) ForEach(readerFunc func(l instrumentation.Library, r export.Reader) error) error { for _, acPair := range c.accumulatorList() { reader := acPair.checkpointer.Reader() // TODO: We should not fail fast; instead accumulate errors. if err := func() error { reader.RLock() defer reader.RUnlock() return readerFunc(acPair.library, reader) }(); err != nil { return err } } return nil } // IsRunning returns true if the controller was started via Start(), // indicating that the current export.Reader is being kept // up-to-date. func (c *Controller) IsRunning() bool { c.lock.Lock() defer c.lock.Unlock() return c.ticker != nil } // Collect requests a collection. The collection will be skipped if // the last collection is aged less than the configured collection // period. func (c *Controller) Collect(ctx context.Context) error { if c.IsRunning() { // When there's a non-nil ticker, there's a goroutine // computing checkpoints with the collection period. return ErrControllerStarted } if !c.shouldCollect() { return nil } return c.checkpoint(ctx) } // shouldCollect returns true if the collector should collect now, // based on the timestamp, the last collection time, and the // configured period. func (c *Controller) shouldCollect() bool { c.lock.Lock() defer c.lock.Unlock() if c.collectPeriod == 0 { return true } now := c.clock.Now() if now.Sub(c.collectedTime) < c.collectPeriod { return false } c.collectedTime = now return true } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/controller_test.go000066400000000000000000000307561414226744000277130ustar00rootroot00000000000000// 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 basic_test import ( "context" "errors" "fmt" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) const envVar = "OTEL_RESOURCE_ATTRIBUTES" func getMap(t *testing.T, cont *controller.Controller) map[string]float64 { out := processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, cont.ForEach( func(_ instrumentation.Library, reader export.Reader) error { return reader.ForEach( aggregation.CumulativeTemporalitySelector(), func(record export.Record) error { return out.AddRecord(record) }, ) })) return out.Map() } type testContextKey string func testContext() context.Context { ctx := context.Background() return context.WithValue(ctx, testContextKey("A"), "B") } func checkTestContext(t *testing.T, ctx context.Context) { require.Equal(t, "B", ctx.Value(testContextKey("A"))) } func TestControllerUsesResource(t *testing.T) { const envVal = "T=U,key=value" store, err := ottest.SetEnvVariables(map[string]string{ envVar: envVal, }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() cases := []struct { name string options []controller.Option wanted string }{ { name: "explicitly empty resource", options: []controller.Option{controller.WithResource(resource.Empty())}, wanted: envVal, }, { name: "uses default if no resource option", options: nil, wanted: resource.Default().Encoded(attribute.DefaultEncoder()), }, { name: "explicit resource", options: []controller.Option{controller.WithResource(resource.NewSchemaless(attribute.String("R", "S")))}, wanted: "R=S," + envVal, }, { name: "multi resource", options: []controller.Option{ controller.WithResource(resource.NewSchemaless(attribute.String("R", "WRONG"))), controller.WithResource(resource.NewSchemaless(attribute.String("R", "S"))), controller.WithResource(resource.NewSchemaless(attribute.String("W", "X"))), controller.WithResource(resource.NewSchemaless(attribute.String("T", "V"))), }, wanted: "R=S,T=V,W=X,key=value", }, { name: "user override environment", options: []controller.Option{ controller.WithResource(resource.NewSchemaless(attribute.String("T", "V"))), controller.WithResource(resource.NewSchemaless(attribute.String("key", "I win"))), }, wanted: "T=V,key=I win", }, } for _, c := range cases { t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { sel := aggregation.CumulativeTemporalitySelector() exp := processortest.New(sel, attribute.DefaultEncoder()) cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), exp, ), append(c.options, controller.WithExporter(exp))..., ) ctx := context.Background() require.NoError(t, cont.Start(ctx)) ctr := metric.Must(cont.Meter("named")).NewFloat64Counter("calls.sum") ctr.Add(context.Background(), 1.) // Collect once require.NoError(t, cont.Stop(ctx)) expect := map[string]float64{ "calls.sum//" + c.wanted: 1., } require.EqualValues(t, expect, exp.Values()) }) } } func TestStartNoExporter(t *testing.T) { cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), ), controller.WithCollectPeriod(time.Second), controller.WithResource(resource.Empty()), ) mock := controllertest.NewMockClock() cont.SetClock(mock) calls := int64(0) _ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("calls.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { calls++ checkTestContext(t, ctx) result.Observe(calls, attribute.String("A", "B")) }, ) // Collect() has not been called. The controller is unstarted. expect := map[string]float64{} // The time advances, but doesn't change the result (not collected). require.EqualValues(t, expect, getMap(t, cont)) mock.Add(time.Second) require.EqualValues(t, expect, getMap(t, cont)) mock.Add(time.Second) expect = map[string]float64{ "calls.lastvalue/A=B/": 1, } // Collect once ctx := testContext() require.NoError(t, cont.Collect(ctx)) require.EqualValues(t, expect, getMap(t, cont)) mock.Add(time.Second) require.EqualValues(t, expect, getMap(t, cont)) mock.Add(time.Second) // Again expect = map[string]float64{ "calls.lastvalue/A=B/": 2, } require.NoError(t, cont.Collect(ctx)) require.EqualValues(t, expect, getMap(t, cont)) mock.Add(time.Second) require.EqualValues(t, expect, getMap(t, cont)) // Start the controller require.NoError(t, cont.Start(ctx)) for i := 1; i <= 3; i++ { expect = map[string]float64{ "calls.lastvalue/A=B/": 2 + float64(i), } mock.Add(time.Second) require.EqualValues(t, expect, getMap(t, cont)) } } func TestObserverCanceled(t *testing.T) { cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), ), controller.WithCollectPeriod(0), controller.WithCollectTimeout(time.Millisecond), controller.WithResource(resource.Empty()), ) calls := int64(0) _ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { <-ctx.Done() calls++ result.Observe(calls) }, ) // This relies on the context timing out err := cont.Collect(context.Background()) require.Error(t, err) require.True(t, errors.Is(err, context.DeadlineExceeded)) expect := map[string]float64{ "done.lastvalue//": 1, } require.EqualValues(t, expect, getMap(t, cont)) } func TestObserverContext(t *testing.T) { cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), ), controller.WithCollectTimeout(0), controller.WithResource(resource.Empty()), ) _ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { time.Sleep(10 * time.Millisecond) checkTestContext(t, ctx) result.Observe(1) }, ) ctx := testContext() require.NoError(t, cont.Collect(ctx)) expect := map[string]float64{ "done.lastvalue//": 1, } require.EqualValues(t, expect, getMap(t, cont)) } type blockingExporter struct { calls int exporter *processortest.Exporter } func newBlockingExporter() *blockingExporter { return &blockingExporter{ exporter: processortest.New( aggregation.CumulativeTemporalitySelector(), attribute.DefaultEncoder(), ), } } func (b *blockingExporter) Export(ctx context.Context, res *resource.Resource, output export.InstrumentationLibraryReader) error { var err error _ = b.exporter.Export(ctx, res, output) if b.calls == 0 { // timeout once <-ctx.Done() err = ctx.Err() } b.calls++ return err } func (*blockingExporter) TemporalityFor(*sdkapi.Descriptor, aggregation.Kind) aggregation.Temporality { return aggregation.CumulativeTemporality } func TestExportTimeout(t *testing.T) { exporter := newBlockingExporter() cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), ), controller.WithCollectPeriod(time.Second), controller.WithPushTimeout(time.Millisecond), controller.WithExporter(exporter), controller.WithResource(resource.Empty()), ) mock := controllertest.NewMockClock() cont.SetClock(mock) calls := int64(0) _ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { calls++ result.Observe(calls) }, ) require.NoError(t, cont.Start(context.Background())) // Initial empty state expect := map[string]float64{} require.EqualValues(t, expect, exporter.exporter.Values()) // Collect after 1s, timeout mock.Add(time.Second) err := testHandler.Flush() require.Error(t, err) require.True(t, errors.Is(err, context.DeadlineExceeded)) expect = map[string]float64{ "one.lastvalue//": 1, } require.EqualValues(t, expect, exporter.exporter.Values()) // Collect again mock.Add(time.Second) expect = map[string]float64{ "one.lastvalue//": 2, } require.EqualValues(t, expect, exporter.exporter.Values()) err = testHandler.Flush() require.NoError(t, err) } func TestCollectAfterStopThenStartAgain(t *testing.T) { exp := processortest.New( aggregation.CumulativeTemporalitySelector(), attribute.DefaultEncoder(), ) cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), exp, ), controller.WithCollectPeriod(time.Second), controller.WithExporter(exp), controller.WithResource(resource.Empty()), ) mock := controllertest.NewMockClock() cont.SetClock(mock) calls := 0 _ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { calls++ result.Observe(int64(calls)) }, ) // No collections happen (because mock clock does not advance): require.NoError(t, cont.Start(context.Background())) require.True(t, cont.IsRunning()) // There's one collection run by Stop(): require.NoError(t, cont.Stop(context.Background())) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 1, }, exp.Values()) require.NoError(t, testHandler.Flush()) // Manual collect after Stop still works, subject to // CollectPeriod. require.NoError(t, cont.Collect(context.Background())) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 2, }, getMap(t, cont)) require.NoError(t, testHandler.Flush()) require.False(t, cont.IsRunning()) // Start again, see that collection proceeds. However, // explicit collection should still fail. require.NoError(t, cont.Start(context.Background())) require.True(t, cont.IsRunning()) err := cont.Collect(context.Background()) require.Error(t, err) require.Equal(t, controller.ErrControllerStarted, err) require.NoError(t, cont.Stop(context.Background())) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 3, }, exp.Values()) require.False(t, cont.IsRunning()) // Time has not advanced yet. Now let the ticker perform // collection: require.NoError(t, cont.Start(context.Background())) mock.Add(time.Second) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 4, }, exp.Values()) mock.Add(time.Second) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 5, }, exp.Values()) require.NoError(t, cont.Stop(context.Background())) require.EqualValues(t, map[string]float64{ "one.lastvalue//": 6, }, exp.Values()) } func TestRegistryFunction(t *testing.T) { exp := processortest.New( aggregation.CumulativeTemporalitySelector(), attribute.DefaultEncoder(), ) cont := controller.New( processor.NewFactory( processortest.AggregatorSelector(), exp, ), controller.WithCollectPeriod(time.Second), controller.WithExporter(exp), controller.WithResource(resource.Empty()), ) m1 := cont.Meter("test") m2 := cont.Meter("test") require.NotNil(t, m1) require.Equal(t, m1, m2) c1, err := m1.NewInt64Counter("counter.sum") require.NoError(t, err) c2, err := m1.NewInt64Counter("counter.sum") require.NoError(t, err) require.Equal(t, c1, c2) ctx := context.Background() require.NoError(t, cont.Start(ctx)) c1.Add(ctx, 10) c2.Add(ctx, 10) require.NoError(t, cont.Stop(ctx)) require.EqualValues(t, map[string]float64{ "counter.sum//": 20, }, exp.Values()) } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/pull_test.go000066400000000000000000000076671414226744000265110ustar00rootroot00000000000000// 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 basic_test import ( "context" "runtime" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) func TestPullNoCollect(t *testing.T) { puller := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), processor.WithMemory(true), ), controller.WithCollectPeriod(0), controller.WithResource(resource.Empty()), ) ctx := context.Background() meter := puller.Meter("nocache") counter := metric.Must(meter).NewInt64Counter("counter.sum") counter.Add(ctx, 10, attribute.String("A", "B")) require.NoError(t, puller.Collect(ctx)) records := processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, controllertest.ReadAll(puller, aggregation.CumulativeTemporalitySelector(), records.AddInstrumentationLibraryRecord)) require.EqualValues(t, map[string]float64{ "counter.sum/A=B/": 10, }, records.Map()) counter.Add(ctx, 10, attribute.String("A", "B")) require.NoError(t, puller.Collect(ctx)) records = processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, controllertest.ReadAll(puller, aggregation.CumulativeTemporalitySelector(), records.AddInstrumentationLibraryRecord)) require.EqualValues(t, map[string]float64{ "counter.sum/A=B/": 20, }, records.Map()) } func TestPullWithCollect(t *testing.T) { puller := controller.New( processor.NewFactory( processortest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector(), processor.WithMemory(true), ), controller.WithCollectPeriod(time.Second), controller.WithResource(resource.Empty()), ) mock := controllertest.NewMockClock() puller.SetClock(mock) ctx := context.Background() meter := puller.Meter("nocache") counter := metric.Must(meter).NewInt64Counter("counter.sum") counter.Add(ctx, 10, attribute.String("A", "B")) require.NoError(t, puller.Collect(ctx)) records := processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, controllertest.ReadAll(puller, aggregation.CumulativeTemporalitySelector(), records.AddInstrumentationLibraryRecord)) require.EqualValues(t, map[string]float64{ "counter.sum/A=B/": 10, }, records.Map()) counter.Add(ctx, 10, attribute.String("A", "B")) // Cached value! require.NoError(t, puller.Collect(ctx)) records = processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, controllertest.ReadAll(puller, aggregation.CumulativeTemporalitySelector(), records.AddInstrumentationLibraryRecord)) require.EqualValues(t, map[string]float64{ "counter.sum/A=B/": 10, }, records.Map()) mock.Add(time.Second) runtime.Gosched() // Re-computed value! require.NoError(t, puller.Collect(ctx)) records = processortest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, controllertest.ReadAll(puller, aggregation.CumulativeTemporalitySelector(), records.AddInstrumentationLibraryRecord)) require.EqualValues(t, map[string]float64{ "counter.sum/A=B/": 20, }, records.Map()) } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/basic/push_test.go000066400000000000000000000132761414226744000265050ustar00rootroot00000000000000// 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 basic_test import ( "context" "errors" "fmt" "runtime" "sync" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) var testResource = resource.NewSchemaless(attribute.String("R", "V")) type handler struct { sync.Mutex err error } func (h *handler) Handle(err error) { h.Lock() h.err = err h.Unlock() } func (h *handler) Flush() error { h.Lock() err := h.err h.err = nil h.Unlock() return err } var testHandler *handler func init() { testHandler = new(handler) otel.SetErrorHandler(testHandler) } func newExporter() *processortest.Exporter { return processortest.New( aggregation.StatelessTemporalitySelector(), attribute.DefaultEncoder(), ) } func newCheckpointerFactory() export.CheckpointerFactory { return processortest.NewCheckpointerFactory( processortest.AggregatorSelector(), attribute.DefaultEncoder(), ) } func TestPushDoubleStop(t *testing.T) { ctx := context.Background() exporter := newExporter() checkpointer := newCheckpointerFactory() p := controller.New(checkpointer, controller.WithExporter(exporter)) require.NoError(t, p.Start(ctx)) require.NoError(t, p.Stop(ctx)) require.NoError(t, p.Stop(ctx)) } func TestPushDoubleStart(t *testing.T) { ctx := context.Background() exporter := newExporter() checkpointer := newCheckpointerFactory() p := controller.New(checkpointer, controller.WithExporter(exporter)) require.NoError(t, p.Start(ctx)) err := p.Start(ctx) require.Error(t, err) require.True(t, errors.Is(err, controller.ErrControllerStarted)) require.NoError(t, p.Stop(ctx)) } func TestPushTicker(t *testing.T) { exporter := newExporter() checkpointer := newCheckpointerFactory() p := controller.New( checkpointer, controller.WithExporter(exporter), controller.WithCollectPeriod(time.Second), controller.WithResource(testResource), ) meter := p.Meter("name") mock := controllertest.NewMockClock() p.SetClock(mock) ctx := context.Background() counter := metric.Must(meter).NewInt64Counter("counter.sum") require.NoError(t, p.Start(ctx)) counter.Add(ctx, 3) require.EqualValues(t, map[string]float64{}, exporter.Values()) mock.Add(time.Second) runtime.Gosched() require.EqualValues(t, map[string]float64{ "counter.sum//R=V": 3, }, exporter.Values()) require.Equal(t, 1, exporter.ExportCount()) exporter.Reset() counter.Add(ctx, 7) mock.Add(time.Second) runtime.Gosched() require.EqualValues(t, map[string]float64{ "counter.sum//R=V": 10, }, exporter.Values()) require.Equal(t, 1, exporter.ExportCount()) exporter.Reset() require.NoError(t, p.Stop(ctx)) } func TestPushExportError(t *testing.T) { injector := func(name string, e error) func(r export.Record) error { return func(r export.Record) error { if r.Descriptor().Name() == name { return e } return nil } } var errAggregator = fmt.Errorf("unexpected error") var tests = []struct { name string injectedError error expected map[string]float64 expectedError error }{ {"errNone", nil, map[string]float64{ "counter1.sum/X=Y/R=V": 3, "counter2.sum//R=V": 5, }, nil}, {"errNoData", aggregation.ErrNoData, map[string]float64{ "counter2.sum//R=V": 5, }, nil}, {"errUnexpected", errAggregator, map[string]float64{}, errAggregator}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { exporter := newExporter() exporter.InjectErr = injector("counter1.sum", tt.injectedError) // This test validates the error handling // behavior of the basic Processor is honored // by the push processor. checkpointer := processor.NewFactory(processortest.AggregatorSelector(), exporter) p := controller.New( checkpointer, controller.WithExporter(exporter), controller.WithCollectPeriod(time.Second), controller.WithResource(testResource), ) mock := controllertest.NewMockClock() p.SetClock(mock) ctx := context.Background() meter := p.Meter("name") counter1 := metric.Must(meter).NewInt64Counter("counter1.sum") counter2 := metric.Must(meter).NewInt64Counter("counter2.sum") require.NoError(t, p.Start(ctx)) runtime.Gosched() counter1.Add(ctx, 3, attribute.String("X", "Y")) counter2.Add(ctx, 5) require.Equal(t, 0, exporter.ExportCount()) require.Nil(t, testHandler.Flush()) mock.Add(time.Second) runtime.Gosched() require.Equal(t, 1, exporter.ExportCount()) if tt.expectedError == nil { require.EqualValues(t, tt.expected, exporter.Values()) require.NoError(t, testHandler.Flush()) } else { err := testHandler.Flush() require.Error(t, err) require.Equal(t, tt.expectedError, err) } require.NoError(t, p.Stop(ctx)) }) } } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/controllertest/000077500000000000000000000000001414226744000261315ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/controller/controllertest/test.go000066400000000000000000000040611414226744000274400ustar00rootroot00000000000000// 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 controllertest // import "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" import ( "time" "github.com/benbjohnson/clock" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" ) type MockClock struct { mock *clock.Mock } type MockTicker struct { ticker *clock.Ticker } var _ controllerTime.Clock = MockClock{} var _ controllerTime.Ticker = MockTicker{} func NewMockClock() MockClock { return MockClock{clock.NewMock()} } func (c MockClock) Now() time.Time { return c.mock.Now() } func (c MockClock) Ticker(period time.Duration) controllerTime.Ticker { return MockTicker{c.mock.Ticker(period)} } func (c MockClock) Add(d time.Duration) { c.mock.Add(d) } func (t MockTicker) Stop() { t.ticker.Stop() } func (t MockTicker) C() <-chan time.Time { return t.ticker.C } // ReadAll is a helper for tests that want a flat iterator over all // metrics instead of a two-level iterator (instrumentation library, // metric). func ReadAll( reader export.InstrumentationLibraryReader, kind aggregation.TemporalitySelector, apply func(instrumentation.Library, export.Record) error, ) error { return reader.ForEach(func(library instrumentation.Library, reader export.Reader) error { return reader.ForEach(kind, func(record export.Record) error { return apply(library, record) }) }) } golang-opentelemetry-otel-1.1.0/sdk/metric/controller/time/000077500000000000000000000000001414226744000240045ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/controller/time/time.go000066400000000000000000000025271414226744000252770ustar00rootroot00000000000000// 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 time // import "go.opentelemetry.io/otel/sdk/metric/controller/time" import ( "time" lib "time" ) // Several types below are created to match "github.com/benbjohnson/clock" // so that it remains a test-only dependency. type Clock interface { Now() lib.Time Ticker(duration lib.Duration) Ticker } type Ticker interface { Stop() C() <-chan lib.Time } type RealClock struct { } type RealTicker struct { ticker *lib.Ticker } var _ Clock = RealClock{} var _ Ticker = RealTicker{} func (RealClock) Now() time.Time { return time.Now() } func (RealClock) Ticker(period time.Duration) Ticker { return RealTicker{time.NewTicker(period)} } func (t RealTicker) Stop() { t.ticker.Stop() } func (t RealTicker) C() <-chan time.Time { return t.ticker.C } golang-opentelemetry-otel-1.1.0/sdk/metric/correct_test.go000066400000000000000000000405721414226744000237220ustar00rootroot00000000000000// 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 metric_test import ( "context" "fmt" "math" "sync" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" metricsdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" ) var Must = metric.Must type handler struct { sync.Mutex err error } func (h *handler) Handle(err error) { h.Lock() h.err = err h.Unlock() } func (h *handler) Reset() { h.Lock() h.err = nil h.Unlock() } func (h *handler) Flush() error { h.Lock() err := h.err h.err = nil h.Unlock() return err } var testHandler *handler func init() { testHandler = new(handler) otel.SetErrorHandler(testHandler) } type testSelector struct { selector export.AggregatorSelector newAggCount int } func (ts *testSelector) AggregatorFor(desc *sdkapi.Descriptor, aggPtrs ...*export.Aggregator) { ts.newAggCount += len(aggPtrs) processortest.AggregatorSelector().AggregatorFor(desc, aggPtrs...) } func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector, *processortest.Processor) { testHandler.Reset() testSelector := &testSelector{selector: processortest.AggregatorSelector()} processor := processortest.NewProcessor( testSelector, attribute.DefaultEncoder(), ) accum := metricsdk.NewAccumulator( processor, ) meter := metric.WrapMeterImpl(accum) return meter, accum, testSelector, processor } func TestInputRangeCounter(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) counter := Must(meter).NewInt64Counter("name.sum") counter.Add(ctx, -1) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) checkpointed := sdk.Collect(ctx) require.Equal(t, 0, checkpointed) processor.Reset() counter.Add(ctx, 1) checkpointed = sdk.Collect(ctx) require.Equal(t, map[string]float64{ "name.sum//": 1, }, processor.Values()) require.Equal(t, 1, checkpointed) require.Nil(t, testHandler.Flush()) } func TestInputRangeUpDownCounter(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) counter := Must(meter).NewInt64UpDownCounter("name.sum") counter.Add(ctx, -1) counter.Add(ctx, -1) counter.Add(ctx, 2) counter.Add(ctx, 1) checkpointed := sdk.Collect(ctx) require.Equal(t, map[string]float64{ "name.sum//": 1, }, processor.Values()) require.Equal(t, 1, checkpointed) require.Nil(t, testHandler.Flush()) } func TestInputRangeHistogram(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) histogram := Must(meter).NewFloat64Histogram("name.exact") histogram.Record(ctx, math.NaN()) require.Equal(t, aggregation.ErrNaNInput, testHandler.Flush()) checkpointed := sdk.Collect(ctx) require.Equal(t, 0, checkpointed) histogram.Record(ctx, 1) histogram.Record(ctx, 2) processor.Reset() checkpointed = sdk.Collect(ctx) require.Equal(t, map[string]float64{ "name.exact//": 3, }, processor.Values()) require.Equal(t, 1, checkpointed) require.Nil(t, testHandler.Flush()) } func TestDisabledInstrument(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) histogram := Must(meter).NewFloat64Histogram("name.disabled") histogram.Record(ctx, -1) checkpointed := sdk.Collect(ctx) require.Equal(t, 0, checkpointed) require.Equal(t, map[string]float64{}, processor.Values()) } func TestRecordNaN(t *testing.T) { ctx := context.Background() meter, _, _, _ := newSDK(t) c := Must(meter).NewFloat64Counter("name.sum") require.Nil(t, testHandler.Flush()) c.Add(ctx, math.NaN()) require.Error(t, testHandler.Flush()) } func TestSDKLabelsDeduplication(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) counter := Must(meter).NewInt64Counter("name.sum") const ( maxKeys = 21 keySets = 2 repeats = 3 ) var keysA []attribute.Key var keysB []attribute.Key for i := 0; i < maxKeys; i++ { keysA = append(keysA, attribute.Key(fmt.Sprintf("A%03d", i))) keysB = append(keysB, attribute.Key(fmt.Sprintf("B%03d", i))) } allExpect := map[string]float64{} for numKeys := 0; numKeys < maxKeys; numKeys++ { var kvsA []attribute.KeyValue var kvsB []attribute.KeyValue for r := 0; r < repeats; r++ { for i := 0; i < numKeys; i++ { kvsA = append(kvsA, keysA[i].Int(r)) kvsB = append(kvsB, keysB[i].Int(r)) } } var expectA []attribute.KeyValue var expectB []attribute.KeyValue for i := 0; i < numKeys; i++ { expectA = append(expectA, keysA[i].Int(repeats-1)) expectB = append(expectB, keysB[i].Int(repeats-1)) } counter.Add(ctx, 1, kvsA...) counter.Add(ctx, 1, kvsA...) format := func(attrs []attribute.KeyValue) string { str := attribute.DefaultEncoder().Encode(newSetIter(attrs...)) return fmt.Sprint("name.sum/", str, "/") } allExpect[format(expectA)] += 2 if numKeys != 0 { // In this case A and B sets are the same. counter.Add(ctx, 1, kvsB...) counter.Add(ctx, 1, kvsB...) allExpect[format(expectB)] += 2 } } sdk.Collect(ctx) require.EqualValues(t, allExpect, processor.Values()) } func newSetIter(kvs ...attribute.KeyValue) attribute.Iterator { labels := attribute.NewSet(kvs...) return labels.Iter() } func TestDefaultLabelEncoder(t *testing.T) { encoder := attribute.DefaultEncoder() encoded := encoder.Encode(newSetIter(attribute.String("A", "B"), attribute.String("C", "D"))) require.Equal(t, `A=B,C=D`, encoded) encoded = encoder.Encode(newSetIter(attribute.String("A", "B,c=d"), attribute.String(`C\`, "D"))) require.Equal(t, `A=B\,c\=d,C\\=D`, encoded) encoded = encoder.Encode(newSetIter(attribute.String(`\`, `=`), attribute.String(`,`, `\`))) require.Equal(t, `\,=\\,\\=\=`, encoded) // Note: the label encoder does not sort or de-dup values, // that is done in Labels(...). encoded = encoder.Encode(newSetIter( attribute.Int("I", 1), attribute.Int64("I64", 1), attribute.Float64("F64", 1), attribute.Float64("F64", 1), attribute.String("S", "1"), attribute.Bool("B", true), )) require.Equal(t, "B=true,F64=1,I=1,I64=1,S=1", encoded) } func TestObserverCollection(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) mult := 1 _ = Must(meter).NewFloat64GaugeObserver("float.gauge.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(float64(mult), attribute.String("A", "B")) // last value wins result.Observe(float64(-mult), attribute.String("A", "B")) result.Observe(float64(-mult), attribute.String("C", "D")) }) _ = Must(meter).NewInt64GaugeObserver("int.gauge.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(int64(-mult), attribute.String("A", "B")) result.Observe(int64(mult)) // last value wins result.Observe(int64(mult), attribute.String("A", "B")) result.Observe(int64(mult)) }) _ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(float64(mult), attribute.String("A", "B")) result.Observe(float64(2*mult), attribute.String("A", "B")) result.Observe(float64(mult), attribute.String("C", "D")) }) _ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(int64(2*mult), attribute.String("A", "B")) result.Observe(int64(mult)) // last value wins result.Observe(int64(mult), attribute.String("A", "B")) result.Observe(int64(mult)) }) _ = Must(meter).NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(float64(mult), attribute.String("A", "B")) result.Observe(float64(-2*mult), attribute.String("A", "B")) result.Observe(float64(mult), attribute.String("C", "D")) }) _ = Must(meter).NewInt64UpDownCounterObserver("int.updowncounterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(int64(2*mult), attribute.String("A", "B")) result.Observe(int64(mult)) // last value wins result.Observe(int64(mult), attribute.String("A", "B")) result.Observe(int64(-mult)) }) _ = Must(meter).NewInt64GaugeObserver("empty.gauge.sum", func(_ context.Context, result metric.Int64ObserverResult) { }) for mult = 0; mult < 3; mult++ { processor.Reset() collected := sdk.Collect(ctx) require.Equal(t, collected, len(processor.Values())) mult := float64(mult) require.EqualValues(t, map[string]float64{ "float.gauge.lastvalue/A=B/": -mult, "float.gauge.lastvalue/C=D/": -mult, "int.gauge.lastvalue//": mult, "int.gauge.lastvalue/A=B/": mult, "float.counterobserver.sum/A=B/": 2 * mult, "float.counterobserver.sum/C=D/": mult, "int.counterobserver.sum//": mult, "int.counterobserver.sum/A=B/": mult, "float.updowncounterobserver.sum/A=B/": -2 * mult, "float.updowncounterobserver.sum/C=D/": mult, "int.updowncounterobserver.sum//": -mult, "int.updowncounterobserver.sum/A=B/": mult, }, processor.Values()) } } func TestCounterObserverInputRange(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) // TODO: these tests are testing for negative values, not for _descending values_. Fix. _ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(-2, attribute.String("A", "B")) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) result.Observe(-1, attribute.String("C", "D")) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) }) _ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(-1, attribute.String("A", "B")) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) result.Observe(-1) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) }) collected := sdk.Collect(ctx) require.Equal(t, 0, collected) require.EqualValues(t, map[string]float64{}, processor.Values()) // check that the error condition was reset require.NoError(t, testHandler.Flush()) } func TestObserverBatch(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) var floatGaugeObs metric.Float64GaugeObserver var intGaugeObs metric.Int64GaugeObserver var floatCounterObs metric.Float64CounterObserver var intCounterObs metric.Int64CounterObserver var floatUpDownCounterObs metric.Float64UpDownCounterObserver var intUpDownCounterObs metric.Int64UpDownCounterObserver var batch = Must(meter).NewBatchObserver( func(_ context.Context, result metric.BatchObserverResult) { result.Observe( []attribute.KeyValue{ attribute.String("A", "B"), }, floatGaugeObs.Observation(1), floatGaugeObs.Observation(-1), intGaugeObs.Observation(-1), intGaugeObs.Observation(1), floatCounterObs.Observation(1000), intCounterObs.Observation(100), floatUpDownCounterObs.Observation(-1000), intUpDownCounterObs.Observation(-100), ) result.Observe( []attribute.KeyValue{ attribute.String("C", "D"), }, floatGaugeObs.Observation(-1), floatCounterObs.Observation(-1), floatUpDownCounterObs.Observation(-1), ) result.Observe( nil, intGaugeObs.Observation(1), intGaugeObs.Observation(1), intCounterObs.Observation(10), floatCounterObs.Observation(1.1), intUpDownCounterObs.Observation(10), ) }) floatGaugeObs = batch.NewFloat64GaugeObserver("float.gauge.lastvalue") intGaugeObs = batch.NewInt64GaugeObserver("int.gauge.lastvalue") floatCounterObs = batch.NewFloat64CounterObserver("float.counterobserver.sum") intCounterObs = batch.NewInt64CounterObserver("int.counterobserver.sum") floatUpDownCounterObs = batch.NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum") intUpDownCounterObs = batch.NewInt64UpDownCounterObserver("int.updowncounterobserver.sum") collected := sdk.Collect(ctx) require.Equal(t, collected, len(processor.Values())) require.EqualValues(t, map[string]float64{ "float.counterobserver.sum//": 1.1, "float.counterobserver.sum/A=B/": 1000, "int.counterobserver.sum//": 10, "int.counterobserver.sum/A=B/": 100, "int.updowncounterobserver.sum/A=B/": -100, "float.updowncounterobserver.sum/A=B/": -1000, "int.updowncounterobserver.sum//": 10, "float.updowncounterobserver.sum/C=D/": -1, "float.gauge.lastvalue/A=B/": -1, "float.gauge.lastvalue/C=D/": -1, "int.gauge.lastvalue//": 1, "int.gauge.lastvalue/A=B/": 1, }, processor.Values()) } func TestRecordBatch(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) counter1 := Must(meter).NewInt64Counter("int64.sum") counter2 := Must(meter).NewFloat64Counter("float64.sum") histogram1 := Must(meter).NewInt64Histogram("int64.exact") histogram2 := Must(meter).NewFloat64Histogram("float64.exact") sdk.RecordBatch( ctx, []attribute.KeyValue{ attribute.String("A", "B"), attribute.String("C", "D"), }, counter1.Measurement(1), counter2.Measurement(2), histogram1.Measurement(3), histogram2.Measurement(4), ) sdk.Collect(ctx) require.EqualValues(t, map[string]float64{ "int64.sum/A=B,C=D/": 1, "float64.sum/A=B,C=D/": 2, "int64.exact/A=B,C=D/": 3, "float64.exact/A=B,C=D/": 4, }, processor.Values()) } // TestRecordPersistence ensures that a direct-called instrument that // is repeatedly used each interval results in a persistent record, so // that its encoded labels will be cached across collection intervals. func TestRecordPersistence(t *testing.T) { ctx := context.Background() meter, sdk, selector, _ := newSDK(t) c := Must(meter).NewFloat64Counter("name.sum") b := c.Bind(attribute.String("bound", "true")) uk := attribute.String("bound", "false") for i := 0; i < 100; i++ { c.Add(ctx, 1, uk) b.Add(ctx, 1) sdk.Collect(ctx) } require.Equal(t, 4, selector.newAggCount) } func TestIncorrectInstruments(t *testing.T) { // The Batch observe/record APIs are susceptible to // uninitialized instruments. var counter metric.Int64Counter var observer metric.Int64GaugeObserver ctx := context.Background() meter, sdk, _, processor := newSDK(t) // Now try with uninitialized instruments. meter.RecordBatch(ctx, nil, counter.Measurement(1)) meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { result.Observe(nil, observer.Observation(1)) }) collected := sdk.Collect(ctx) require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush()) require.Equal(t, 0, collected) // Now try with instruments from another SDK. var noopMeter metric.Meter counter = metric.Must(noopMeter).NewInt64Counter("name.sum") observer = metric.Must(noopMeter).NewBatchObserver( func(context.Context, metric.BatchObserverResult) {}, ).NewInt64GaugeObserver("observer") meter.RecordBatch(ctx, nil, counter.Measurement(1)) meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { result.Observe(nil, observer.Observation(1)) }) collected = sdk.Collect(ctx) require.Equal(t, 0, collected) require.EqualValues(t, map[string]float64{}, processor.Values()) require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush()) } func TestSyncInAsync(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) counter := Must(meter).NewFloat64Counter("counter.sum") _ = Must(meter).NewInt64GaugeObserver("observer.lastvalue", func(ctx context.Context, result metric.Int64ObserverResult) { result.Observe(10) counter.Add(ctx, 100) }, ) sdk.Collect(ctx) require.EqualValues(t, map[string]float64{ "counter.sum//": 100, "observer.lastvalue//": 10, }, processor.Values()) } golang-opentelemetry-otel-1.1.0/sdk/metric/doc.go000066400000000000000000000154711414226744000217670ustar00rootroot00000000000000// 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 metric implements the OpenTelemetry metric API. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. The Accumulator type supports configurable metrics export behavior through a collection of export interfaces that support various export strategies, described below. The OpenTelemetry metric API consists of methods for constructing synchronous and asynchronous instruments. There are two constructors per instrument for the two kinds of number (int64, float64). Synchronous instruments are managed by a sync.Map containing a *record with the current state for each synchronous instrument. A bound instrument encapsulates a direct pointer to the record, allowing bound metric events to bypass a sync.Map lookup. A lock-free algorithm is used to protect against races when adding and removing items from the sync.Map. Asynchronous instruments are managed by an internal AsyncInstrumentState, which coordinates calling batch and single instrument callbacks. Internal Structure Each observer also has its own kind of record stored in the SDK. This record contains a set of recorders for every specific label set used in the callback. A sync.Map maintains the mapping of current instruments and label sets to internal records. To create a new bound instrument, the SDK consults the Map to locate an existing record, otherwise it constructs a new record. The SDK maintains a count of the number of references to each record, ensuring that records are not reclaimed from the Map while they are still active from the user's perspective. Metric collection is performed via a single-threaded call to Collect that sweeps through all records in the SDK, checkpointing their state. When a record is discovered that has no references and has not been updated since the prior collection pass, it is removed from the Map. Both synchronous and asynchronous instruments have an associated aggregator, which maintains the current state resulting from all metric events since its last checkpoint. Aggregators may be lock-free or they may use locking, but they should expect to be called concurrently. Aggregators must be capable of merging with another aggregator of the same type. Export Pipeline While the SDK serves to maintain a current set of records and coordinate collection, the behavior of a metrics export pipeline is configured through the export types in go.opentelemetry.io/otel/sdk/export/metric. It is important to keep in mind the context these interfaces are called from. There are two contexts, instrumentation context, where a user-level goroutine that enters the SDK resulting in a new record, and collection context, where a system-level thread performs a collection pass through the SDK. Descriptor is a struct that describes the metric instrument to the export pipeline, containing the name, units, description, metric kind, number kind (int64 or float64). A Descriptor accompanies metric data as it passes through the export pipeline. The AggregatorSelector interface supports choosing the method of aggregation to apply to a particular instrument, by delegating the construction of an Aggregator to this interface. Given the Descriptor, the AggregatorFor method returns an implementation of Aggregator. If this interface returns nil, the metric will be disabled. The aggregator should be matched to the capabilities of the exporter. Selecting the aggregator for Adding instruments is relatively straightforward, but many options are available for aggregating distributions from Grouping instruments. Aggregator is an interface which implements a concrete strategy for aggregating metric updates. Several Aggregator implementations are provided by the SDK. Aggregators may be lock-free or use locking, depending on their structure and semantics. Aggregators implement an Update method, called in instrumentation context, to receive a single metric event. Aggregators implement a Checkpoint method, called in collection context, to save a checkpoint of the current state. Aggregators implement a Merge method, also called in collection context, that combines state from two aggregators into one. Each SDK record has an associated aggregator. Processor is an interface which sits between the SDK and an exporter. The Processor embeds an AggregatorSelector, used by the SDK to assign new Aggregators. The Processor supports a Process() API for submitting checkpointed aggregators to the processor, and a Reader() API for producing a complete checkpoint for the exporter. Two default Processor implementations are provided, the "defaultkeys" Processor groups aggregate metrics by their recommended Descriptor.Keys(), the "simple" Processor aggregates metrics at full dimensionality. LabelEncoder is an optional optimization that allows an exporter to provide the serialization logic for labels. This allows avoiding duplicate serialization of labels, once as a unique key in the SDK (or Processor) and once in the exporter. Reader is an interface between the Processor and the Exporter. After completing a collection pass, the Processor.Reader() method returns a Reader, which the Exporter uses to iterate over all the updated metrics. Record is a struct containing the state of an individual exported metric. This is the result of one collection interface for one instrument and one label set. Labels is a struct containing an ordered set of labels, the corresponding unique encoding, and the encoder that produced it. Exporter is the final stage of an export pipeline. It is called with a Reader capable of enumerating all the updated metrics. Controller is not an export interface per se, but it orchestrates the export pipeline. For example, a "push" controller will establish a periodic timer to regularly collect and export metrics. A "pull" controller will await a pull request before initiating metric collection. Either way, the job of the controller is to call the SDK Collect() method, then read the checkpoint, then invoke the exporter. Controllers are expected to implement the public metric.MeterProvider API, meaning they can be installed as the global Meter provider. */ package metric // import "go.opentelemetry.io/otel/sdk/metric" golang-opentelemetry-otel-1.1.0/sdk/metric/go.mod000066400000000000000000000057321414226744000220000ustar00rootroot00000000000000module go.opentelemetry.io/otel/sdk/metric go 1.15 replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools replace go.opentelemetry.io/otel/metric => ../../metric replace go.opentelemetry.io/otel/sdk => ../ replace go.opentelemetry.io/otel/sdk/export/metric => ../export/metric replace go.opentelemetry.io/otel/sdk/metric => ./ replace go.opentelemetry.io/otel/trace => ../../trace require ( github.com/benbjohnson/clock v1.1.0 // do not upgrade to v1.1.x because it would require Go >= 1.15 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 go.opentelemetry.io/otel/internal/metric v0.24.0 go.opentelemetry.io/otel/metric v0.24.0 go.opentelemetry.io/otel/sdk v1.1.0 go.opentelemetry.io/otel/sdk/export/metric v0.24.0 ) replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../../example/fib replace go.opentelemetry.io/otel/schema => ../../schema golang-opentelemetry-otel-1.1.0/sdk/metric/go.sum000066400000000000000000000033741414226744000220250ustar00rootroot00000000000000github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/sdk/metric/histogram_stress_test.go000066400000000000000000000033461414226744000256570ustar00rootroot00000000000000// 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 metric_test import ( "context" "math/rand" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" ) func TestStressInt64Histogram(t *testing.T) { desc := metrictest.NewDescriptor("some_metric", sdkapi.HistogramInstrumentKind, number.Int64Kind) alloc := histogram.New(2, &desc, histogram.WithExplicitBoundaries([]float64{25, 50, 75})) h, ckpt := &alloc[0], &alloc[1] ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() go func() { rnd := rand.New(rand.NewSource(time.Now().Unix())) for { select { case <-ctx.Done(): return default: _ = h.Update(ctx, number.NewInt64Number(rnd.Int63()%100), &desc) } } }() startTime := time.Now() for time.Since(startTime) < time.Second { require.NoError(t, h.SynchronizedMove(ckpt, &desc)) b, _ := ckpt.Histogram() c, _ := ckpt.Count() var realCount uint64 for _, c := range b.Counts { realCount += c } if realCount != c { t.Fail() } } } golang-opentelemetry-otel-1.1.0/sdk/metric/minmaxsumcount_stress_test.go000066400000000000000000000037301414226744000267460ustar00rootroot00000000000000// 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 metric_test import ( "context" "math/rand" "testing" "time" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" ) func TestStressInt64MinMaxSumCount(t *testing.T) { desc := metrictest.NewDescriptor("some_metric", sdkapi.HistogramInstrumentKind, number.Int64Kind) alloc := minmaxsumcount.New(2, &desc) mmsc, ckpt := &alloc[0], &alloc[1] ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { rnd := rand.New(rand.NewSource(time.Now().Unix())) v := rnd.Int63() % 103 for { select { case <-ctx.Done(): return default: _ = mmsc.Update(ctx, number.NewInt64Number(v), &desc) } v++ } }() startTime := time.Now() for time.Since(startTime) < time.Second { _ = mmsc.SynchronizedMove(ckpt, &desc) s, _ := ckpt.Sum() c, _ := ckpt.Count() min, e1 := ckpt.Min() max, e2 := ckpt.Max() if c == 0 && (e1 == nil || e2 == nil || s.AsInt64() != 0) { t.Fail() } if c != 0 { if e1 != nil || e2 != nil { t.Fail() } lo, hi, sum := min.AsInt64(), max.AsInt64(), s.AsInt64() if uint64(hi-lo)+1 != c { t.Fail() } if c == 1 { if lo != hi || lo != sum { t.Fail() } } else { if hi*(hi+1)/2-(lo-1)*lo/2 != sum { t.Fail() } } } } } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/000077500000000000000000000000001414226744000227025ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/processor/basic/000077500000000000000000000000001414226744000237635ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/processor/basic/basic.go000066400000000000000000000303411414226744000253740ustar00rootroot00000000000000// 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 basic // import "go.opentelemetry.io/otel/sdk/metric/processor/basic" import ( "errors" "fmt" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" ) type ( Processor struct { aggregation.TemporalitySelector export.AggregatorSelector state } stateKey struct { // TODO: This code is organized to support multiple // accumulators which could theoretically produce the // data for the same instrument, and this code has // logic to combine data properly from multiple // accumulators. However, the use of // *sdkapi.Descriptor in the stateKey makes such // combination impossible, because each accumulator // allocates its own instruments. This can be fixed // by using the instrument name and kind instead of // the descriptor pointer. See // https://github.com/open-telemetry/opentelemetry-go/issues/862. descriptor *sdkapi.Descriptor distinct attribute.Distinct } stateValue struct { // labels corresponds to the stateKey.distinct field. labels *attribute.Set // updated indicates the last sequence number when this value had // Process() called by an accumulator. updated int64 // stateful indicates that a cumulative aggregation is // being maintained, taken from the process start time. stateful bool // currentOwned indicates that "current" was allocated // by the processor in order to merge results from // multiple Accumulators during a single collection // round, which may happen either because: // (1) multiple Accumulators output the same Accumulation. // (2) one Accumulator is configured with dimensionality reduction. currentOwned bool // current refers to the output from a single Accumulator // (if !currentOwned) or it refers to an Aggregator // owned by the processor used to accumulate multiple // values in a single collection round. current export.Aggregator // delta, if non-nil, refers to an Aggregator owned by // the processor used to compute deltas between // precomputed sums. delta export.Aggregator // cumulative, if non-nil, refers to an Aggregator owned // by the processor used to store the last cumulative // value. cumulative export.Aggregator } state struct { config config // RWMutex implements locking for the `Reader` interface. sync.RWMutex values map[stateKey]*stateValue // Note: the timestamp logic currently assumes all // exports are deltas. processStart time.Time intervalStart time.Time intervalEnd time.Time // startedCollection and finishedCollection are the // number of StartCollection() and FinishCollection() // calls, used to ensure that the sequence of starts // and finishes are correctly balanced. startedCollection int64 finishedCollection int64 } ) var _ export.Processor = &Processor{} var _ export.Checkpointer = &Processor{} var _ export.Reader = &state{} // ErrInconsistentState is returned when the sequence of collection's starts and finishes are incorrectly balanced. var ErrInconsistentState = fmt.Errorf("inconsistent processor state") // ErrInvalidTemporality is returned for unknown metric.Temporality. var ErrInvalidTemporality = fmt.Errorf("invalid aggregation temporality") // New returns a basic Processor that is also a Checkpointer using the provided // AggregatorSelector to select Aggregators. The TemporalitySelector // is consulted to determine the kind(s) of exporter that will consume // data, so that this Processor can prepare to compute Delta or // Cumulative Aggregations as needed. func New(aselector export.AggregatorSelector, tselector aggregation.TemporalitySelector, opts ...Option) *Processor { return NewFactory(aselector, tselector, opts...).NewCheckpointer().(*Processor) } type factory struct { aselector export.AggregatorSelector tselector aggregation.TemporalitySelector config config } func NewFactory(aselector export.AggregatorSelector, tselector aggregation.TemporalitySelector, opts ...Option) export.CheckpointerFactory { var config config for _, opt := range opts { opt.applyProcessor(&config) } return factory{ aselector: aselector, tselector: tselector, config: config, } } var _ export.CheckpointerFactory = factory{} func (f factory) NewCheckpointer() export.Checkpointer { now := time.Now() p := &Processor{ AggregatorSelector: f.aselector, TemporalitySelector: f.tselector, state: state{ values: map[stateKey]*stateValue{}, processStart: now, intervalStart: now, config: f.config, }, } return p } // Process implements export.Processor. func (b *Processor) Process(accum export.Accumulation) error { if b.startedCollection != b.finishedCollection+1 { return ErrInconsistentState } desc := accum.Descriptor() key := stateKey{ descriptor: desc, distinct: accum.Labels().Equivalent(), } agg := accum.Aggregator() // Check if there is an existing value. value, ok := b.state.values[key] if !ok { stateful := b.TemporalityFor(desc, agg.Aggregation().Kind()).MemoryRequired(desc.InstrumentKind()) newValue := &stateValue{ labels: accum.Labels(), updated: b.state.finishedCollection, stateful: stateful, current: agg, } if stateful { if desc.InstrumentKind().PrecomputedSum() { // If we know we need to compute deltas, allocate two aggregators. b.AggregatorFor(desc, &newValue.cumulative, &newValue.delta) } else { // In this case we are certain not to need a delta, only allocate // a cumulative aggregator. b.AggregatorFor(desc, &newValue.cumulative) } } b.state.values[key] = newValue return nil } // Advance the update sequence number. sameCollection := b.state.finishedCollection == value.updated value.updated = b.state.finishedCollection // At this point in the code, we have located an existing // value for some stateKey. This can be because: // // (a) stateful aggregation is being used, the entry was // entered during a prior collection, and this is the first // time processing an accumulation for this stateKey in the // current collection. Since this is the first time // processing an accumulation for this stateKey during this // collection, we don't know yet whether there are multiple // accumulators at work. If there are multiple accumulators, // they'll hit case (b) the second time through. // // (b) multiple accumulators are being used, whether stateful // or not. // // Case (a) occurs when the instrument and the exporter // require memory to work correctly, either because the // instrument reports a PrecomputedSum to a DeltaExporter or // the reverse, a non-PrecomputedSum instrument with a // CumulativeExporter. This logic is encapsulated in // Temporality.MemoryRequired(InstrumentKind). // // Case (b) occurs when the variable `sameCollection` is true, // indicating that the stateKey for Accumulation has already // been seen in the same collection. When this happens, it // implies that multiple Accumulators are being used, or that // a single Accumulator has been configured with a label key // filter. if !sameCollection { if !value.currentOwned { // This is the first Accumulation we've seen // for this stateKey during this collection. // Just keep a reference to the Accumulator's // Aggregator. All the other cases copy // Aggregator state. value.current = agg return nil } return agg.SynchronizedMove(value.current, desc) } // If the current is not owned, take ownership of a copy // before merging below. if !value.currentOwned { tmp := value.current b.AggregatorSelector.AggregatorFor(desc, &value.current) value.currentOwned = true if err := tmp.SynchronizedMove(value.current, desc); err != nil { return err } } // Combine this Accumulation with the prior Accumulation. return value.current.Merge(agg, desc) } // Reader returns the associated Reader. Use the // Reader Locker interface to synchronize access to this // object. The Reader.ForEach() method cannot be called // concurrently with Process(). func (b *Processor) Reader() export.Reader { return &b.state } // StartCollection signals to the Processor one or more Accumulators // will begin calling Process() calls during collection. func (b *Processor) StartCollection() { if b.startedCollection != 0 { b.intervalStart = b.intervalEnd } b.startedCollection++ } // FinishCollection signals to the Processor that a complete // collection has finished and that ForEach will be called to access // the Reader. func (b *Processor) FinishCollection() error { b.intervalEnd = time.Now() if b.startedCollection != b.finishedCollection+1 { return ErrInconsistentState } defer func() { b.finishedCollection++ }() for key, value := range b.values { mkind := key.descriptor.InstrumentKind() stale := value.updated != b.finishedCollection stateless := !value.stateful // The following branch updates stateful aggregators. Skip // these updates if the aggregator is not stateful or if the // aggregator is stale. if stale || stateless { // If this processor does not require memeory, // stale, stateless entries can be removed. // This implies that they were not updated // over the previous full collection interval. if stale && stateless && !b.config.Memory { delete(b.values, key) } continue } // Update Aggregator state to support exporting either a // delta or a cumulative aggregation. var err error if mkind.PrecomputedSum() { if currentSubtractor, ok := value.current.(export.Subtractor); ok { // This line is equivalent to: // value.delta = currentSubtractor - value.cumulative err = currentSubtractor.Subtract(value.cumulative, value.delta, key.descriptor) if err == nil { err = value.current.SynchronizedMove(value.cumulative, key.descriptor) } } else { err = aggregation.ErrNoSubtraction } } else { // This line is equivalent to: // value.cumulative = value.cumulative + value.delta err = value.cumulative.Merge(value.current, key.descriptor) } if err != nil { return err } } return nil } // ForEach iterates through the Reader, passing an // export.Record with the appropriate Cumulative or Delta aggregation // to an exporter. func (b *state) ForEach(exporter aggregation.TemporalitySelector, f func(export.Record) error) error { if b.startedCollection != b.finishedCollection { return ErrInconsistentState } for key, value := range b.values { mkind := key.descriptor.InstrumentKind() var agg aggregation.Aggregation var start time.Time // If the processor does not have Config.Memory and it was not updated // in the prior round, do not visit this value. if !b.config.Memory && value.updated != (b.finishedCollection-1) { continue } aggTemp := exporter.TemporalityFor(key.descriptor, value.current.Aggregation().Kind()) switch aggTemp { case aggregation.CumulativeTemporality: // If stateful, the sum has been computed. If stateless, the // input was already cumulative. Either way, use the checkpointed // value: if value.stateful { agg = value.cumulative.Aggregation() } else { agg = value.current.Aggregation() } start = b.processStart case aggregation.DeltaTemporality: // Precomputed sums are a special case. if mkind.PrecomputedSum() { agg = value.delta.Aggregation() } else { agg = value.current.Aggregation() } start = b.intervalStart default: return fmt.Errorf("%v: %w", aggTemp, ErrInvalidTemporality) } if err := f(export.NewRecord( key.descriptor, value.labels, agg, start, b.intervalEnd, )); err != nil && !errors.Is(err, aggregation.ErrNoData) { return err } } return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/basic/basic_test.go000066400000000000000000000406111414226744000264340ustar00rootroot00000000000000// 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 basic_test import ( "context" "errors" "fmt" "strings" "testing" "time" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" sdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) func requireNotAfter(t *testing.T, t1, t2 time.Time) { require.False(t, t1.After(t2), "expected %v ≤ %v", t1, t2) } // TestProcessor tests all the non-error paths in this package. func TestProcessor(t *testing.T) { type exportCase struct { kind aggregation.Temporality } type instrumentCase struct { kind sdkapi.InstrumentKind } type numberCase struct { kind number.Kind } type aggregatorCase struct { kind aggregation.Kind } for _, tc := range []exportCase{ {kind: aggregation.CumulativeTemporality}, {kind: aggregation.DeltaTemporality}, } { t.Run(tc.kind.String(), func(t *testing.T) { for _, ic := range []instrumentCase{ {kind: sdkapi.CounterInstrumentKind}, {kind: sdkapi.UpDownCounterInstrumentKind}, {kind: sdkapi.HistogramInstrumentKind}, {kind: sdkapi.CounterObserverInstrumentKind}, {kind: sdkapi.UpDownCounterObserverInstrumentKind}, {kind: sdkapi.GaugeObserverInstrumentKind}, } { t.Run(ic.kind.String(), func(t *testing.T) { for _, nc := range []numberCase{ {kind: number.Int64Kind}, {kind: number.Float64Kind}, } { t.Run(nc.kind.String(), func(t *testing.T) { for _, ac := range []aggregatorCase{ {kind: aggregation.SumKind}, {kind: aggregation.MinMaxSumCountKind}, {kind: aggregation.HistogramKind}, {kind: aggregation.LastValueKind}, {kind: aggregation.ExactKind}, } { t.Run(ac.kind.String(), func(t *testing.T) { testProcessor( t, tc.kind, ic.kind, nc.kind, ac.kind, ) }) } }) } }) } }) } } func asNumber(nkind number.Kind, value int64) number.Number { if nkind == number.Int64Kind { return number.NewInt64Number(value) } return number.NewFloat64Number(float64(value)) } func updateFor(t *testing.T, desc *sdkapi.Descriptor, selector export.AggregatorSelector, value int64, labs ...attribute.KeyValue) export.Accumulation { ls := attribute.NewSet(labs...) var agg export.Aggregator selector.AggregatorFor(desc, &agg) require.NoError(t, agg.Update(context.Background(), asNumber(desc.NumberKind(), value), desc)) return export.NewAccumulation(desc, &ls, agg) } func testProcessor( t *testing.T, aggTemp aggregation.Temporality, mkind sdkapi.InstrumentKind, nkind number.Kind, akind aggregation.Kind, ) { // Note: this selector uses the instrument name to dictate // aggregation kind. selector := processorTest.AggregatorSelector() labs1 := []attribute.KeyValue{attribute.String("L1", "V")} labs2 := []attribute.KeyValue{attribute.String("L2", "V")} testBody := func(t *testing.T, hasMemory bool, nAccum, nCheckpoint int) { processor := basic.New(selector, aggregation.ConstantTemporalitySelector(aggTemp), basic.WithMemory(hasMemory)) instSuffix := fmt.Sprint(".", strings.ToLower(akind.String())) desc1 := metrictest.NewDescriptor(fmt.Sprint("inst1", instSuffix), mkind, nkind) desc2 := metrictest.NewDescriptor(fmt.Sprint("inst2", instSuffix), mkind, nkind) for nc := 0; nc < nCheckpoint; nc++ { // The input is 10 per update, scaled by // the number of checkpoints for // cumulative instruments: input := int64(10) cumulativeMultiplier := int64(nc + 1) if mkind.PrecomputedSum() { input *= cumulativeMultiplier } processor.StartCollection() for na := 0; na < nAccum; na++ { _ = processor.Process(updateFor(t, &desc1, selector, input, labs1...)) _ = processor.Process(updateFor(t, &desc2, selector, input, labs2...)) } err := processor.FinishCollection() if err == aggregation.ErrNoSubtraction { var subr export.Aggregator selector.AggregatorFor(&desc1, &subr) _, canSub := subr.(export.Subtractor) // Allow unsupported subraction case only when it is called for. require.True(t, mkind.PrecomputedSum() && aggTemp == aggregation.DeltaTemporality && !canSub) return } else if err != nil { t.Fatal("unexpected FinishCollection error: ", err) } if nc < nCheckpoint-1 { continue } reader := processor.Reader() for _, repetitionAfterEmptyInterval := range []bool{false, true} { if repetitionAfterEmptyInterval { // We're repeating the test after another // interval with no updates. processor.StartCollection() if err := processor.FinishCollection(); err != nil { t.Fatal("unexpected collection error: ", err) } } // Test the final checkpoint state. records1 := processorTest.NewOutput(attribute.DefaultEncoder()) err = reader.ForEach(aggregation.ConstantTemporalitySelector(aggTemp), records1.AddRecord) // Test for an allowed error: if err != nil && err != aggregation.ErrNoSubtraction { t.Fatal("unexpected checkpoint error: ", err) } var multiplier int64 if mkind.Asynchronous() { // Asynchronous tests accumulate results multiply by the // number of Accumulators, unless LastValue aggregation. // If a precomputed sum, we expect cumulative inputs. if mkind.PrecomputedSum() { if aggTemp == aggregation.DeltaTemporality && akind != aggregation.LastValueKind { multiplier = int64(nAccum) } else if akind == aggregation.LastValueKind { multiplier = cumulativeMultiplier } else { multiplier = cumulativeMultiplier * int64(nAccum) } } else { if aggTemp == aggregation.CumulativeTemporality && akind != aggregation.LastValueKind { multiplier = cumulativeMultiplier * int64(nAccum) } else if akind == aggregation.LastValueKind { multiplier = 1 } else { multiplier = int64(nAccum) } } } else { // Synchronous accumulate results from multiple accumulators, // use that number as the baseline multiplier. multiplier = int64(nAccum) if aggTemp == aggregation.CumulativeTemporality { // If a cumulative exporter, include prior checkpoints. multiplier *= cumulativeMultiplier } if akind == aggregation.LastValueKind { // If a last-value aggregator, set multiplier to 1.0. multiplier = 1 } } exp := map[string]float64{} if hasMemory || !repetitionAfterEmptyInterval { exp = map[string]float64{ fmt.Sprintf("inst1%s/L1=V/", instSuffix): float64(multiplier * 10), // labels1 fmt.Sprintf("inst2%s/L2=V/", instSuffix): float64(multiplier * 10), // labels2 } } require.EqualValues(t, exp, records1.Map(), "with repetition=%v", repetitionAfterEmptyInterval) } } } for _, hasMem := range []bool{false, true} { t.Run(fmt.Sprintf("HasMemory=%v", hasMem), func(t *testing.T) { // For 1 to 3 checkpoints: for nAccum := 1; nAccum <= 3; nAccum++ { t.Run(fmt.Sprintf("NumAccum=%d", nAccum), func(t *testing.T) { // For 1 to 3 accumulators: for nCheckpoint := 1; nCheckpoint <= 3; nCheckpoint++ { t.Run(fmt.Sprintf("NumCkpt=%d", nCheckpoint), func(t *testing.T) { testBody(t, hasMem, nAccum, nCheckpoint) }) } }) } }) } } type bogusExporter struct{} func (bogusExporter) TemporalityFor(*sdkapi.Descriptor, aggregation.Kind) aggregation.Temporality { return 100 } func (bogusExporter) Export(context.Context, export.Reader) error { panic("Not called") } func TestBasicInconsistent(t *testing.T) { // Test double-start b := basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) b.StartCollection() b.StartCollection() require.Equal(t, basic.ErrInconsistentState, b.FinishCollection()) // Test finish without start b = basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) require.Equal(t, basic.ErrInconsistentState, b.FinishCollection()) // Test no finish b = basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) b.StartCollection() require.Equal( t, basic.ErrInconsistentState, b.ForEach( aggregation.StatelessTemporalitySelector(), func(export.Record) error { return nil }, ), ) // Test no start b = basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) desc := metrictest.NewDescriptor("inst", sdkapi.CounterInstrumentKind, number.Int64Kind) accum := export.NewAccumulation(&desc, attribute.EmptySet(), aggregatortest.NoopAggregator{}) require.Equal(t, basic.ErrInconsistentState, b.Process(accum)) // Test invalid kind: b = basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) b.StartCollection() require.NoError(t, b.Process(accum)) require.NoError(t, b.FinishCollection()) err := b.ForEach( bogusExporter{}, func(export.Record) error { return nil }, ) require.True(t, errors.Is(err, basic.ErrInvalidTemporality)) } func TestBasicTimestamps(t *testing.T) { beforeNew := time.Now() time.Sleep(time.Nanosecond) b := basic.New(processorTest.AggregatorSelector(), aggregation.StatelessTemporalitySelector()) time.Sleep(time.Nanosecond) afterNew := time.Now() desc := metrictest.NewDescriptor("inst", sdkapi.CounterInstrumentKind, number.Int64Kind) accum := export.NewAccumulation(&desc, attribute.EmptySet(), aggregatortest.NoopAggregator{}) b.StartCollection() _ = b.Process(accum) require.NoError(t, b.FinishCollection()) var start1, end1 time.Time require.NoError(t, b.ForEach(aggregation.StatelessTemporalitySelector(), func(rec export.Record) error { start1 = rec.StartTime() end1 = rec.EndTime() return nil })) // The first start time is set in the constructor. requireNotAfter(t, beforeNew, start1) requireNotAfter(t, start1, afterNew) for i := 0; i < 2; i++ { b.StartCollection() require.NoError(t, b.Process(accum)) require.NoError(t, b.FinishCollection()) var start2, end2 time.Time require.NoError(t, b.ForEach(aggregation.StatelessTemporalitySelector(), func(rec export.Record) error { start2 = rec.StartTime() end2 = rec.EndTime() return nil })) // Subsequent intervals have their start and end aligned. require.Equal(t, start2, end1) requireNotAfter(t, start1, end1) requireNotAfter(t, start2, end2) start1 = start2 end1 = end2 } } func TestStatefulNoMemoryCumulative(t *testing.T) { aggTempSel := aggregation.CumulativeTemporalitySelector() desc := metrictest.NewDescriptor("inst.sum", sdkapi.CounterInstrumentKind, number.Int64Kind) selector := processorTest.AggregatorSelector() processor := basic.New(selector, aggTempSel, basic.WithMemory(false)) reader := processor.Reader() for i := 1; i < 3; i++ { // Empty interval processor.StartCollection() require.NoError(t, processor.FinishCollection()) // Verify zero elements records := processorTest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, reader.ForEach(aggTempSel, records.AddRecord)) require.EqualValues(t, map[string]float64{}, records.Map()) // Add 10 processor.StartCollection() _ = processor.Process(updateFor(t, &desc, selector, 10, attribute.String("A", "B"))) require.NoError(t, processor.FinishCollection()) // Verify one element records = processorTest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, reader.ForEach(aggTempSel, records.AddRecord)) require.EqualValues(t, map[string]float64{ "inst.sum/A=B/": float64(i * 10), }, records.Map()) } } func TestStatefulNoMemoryDelta(t *testing.T) { aggTempSel := aggregation.DeltaTemporalitySelector() desc := metrictest.NewDescriptor("inst.sum", sdkapi.CounterObserverInstrumentKind, number.Int64Kind) selector := processorTest.AggregatorSelector() processor := basic.New(selector, aggTempSel, basic.WithMemory(false)) reader := processor.Reader() for i := 1; i < 3; i++ { // Empty interval processor.StartCollection() require.NoError(t, processor.FinishCollection()) // Verify zero elements records := processorTest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, reader.ForEach(aggTempSel, records.AddRecord)) require.EqualValues(t, map[string]float64{}, records.Map()) // Add 10 processor.StartCollection() _ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) require.NoError(t, processor.FinishCollection()) // Verify one element records = processorTest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, reader.ForEach(aggTempSel, records.AddRecord)) require.EqualValues(t, map[string]float64{ "inst.sum/A=B/": 10, }, records.Map()) } } func TestMultiObserverSum(t *testing.T) { for _, aggTempSel := range []aggregation.TemporalitySelector{ aggregation.CumulativeTemporalitySelector(), aggregation.DeltaTemporalitySelector(), } { desc := metrictest.NewDescriptor("observe.sum", sdkapi.CounterObserverInstrumentKind, number.Int64Kind) selector := processorTest.AggregatorSelector() processor := basic.New(selector, aggTempSel, basic.WithMemory(false)) reader := processor.Reader() for i := 1; i < 3; i++ { // Add i*10*3 times processor.StartCollection() _ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) _ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) _ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) require.NoError(t, processor.FinishCollection()) // Multiplier is 1 for deltas, otherwise i. multiplier := i if aggTempSel.TemporalityFor(&desc, aggregation.SumKind) == aggregation.DeltaTemporality { multiplier = 1 } // Verify one element records := processorTest.NewOutput(attribute.DefaultEncoder()) require.NoError(t, reader.ForEach(aggTempSel, records.AddRecord)) require.EqualValues(t, map[string]float64{ "observe.sum/A=B/": float64(3 * 10 * multiplier), }, records.Map()) } } } func TestCounterObserverEndToEnd(t *testing.T) { ctx := context.Background() eselector := aggregation.CumulativeTemporalitySelector() proc := basic.New( processorTest.AggregatorSelector(), eselector, ) accum := sdk.NewAccumulator(proc) meter := metric.WrapMeterImpl(accum) var calls int64 metric.Must(meter).NewInt64CounterObserver("observer.sum", func(_ context.Context, result metric.Int64ObserverResult) { calls++ result.Observe(calls) }, ) reader := proc.Reader() var startTime [3]time.Time var endTime [3]time.Time for i := range startTime { data := proc.Reader() data.Lock() proc.StartCollection() accum.Collect(ctx) require.NoError(t, proc.FinishCollection()) exporter := processortest.New(eselector, attribute.DefaultEncoder()) require.NoError(t, exporter.Export(ctx, resource.Empty(), processortest.OneInstrumentationLibraryReader( instrumentation.Library{ Name: "test", }, reader))) require.EqualValues(t, map[string]float64{ "observer.sum//": float64(i + 1), }, exporter.Values()) var record export.Record require.NoError(t, data.ForEach(eselector, func(r export.Record) error { record = r return nil })) startTime[i] = record.StartTime() endTime[i] = record.EndTime() data.Unlock() } require.Equal(t, startTime[0], startTime[1]) require.Equal(t, startTime[0], startTime[2]) requireNotAfter(t, endTime[0], endTime[1]) requireNotAfter(t, endTime[1], endTime[2]) } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/basic/config.go000066400000000000000000000026671414226744000255720ustar00rootroot00000000000000// 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 basic // import "go.opentelemetry.io/otel/sdk/metric/processor/basic" // config contains the options for configuring a basic metric processor. type config struct { // Memory controls whether the processor remembers metric // instruments and label sets that were previously reported. // When Memory is true, Reader.ForEach() will visit // metrics that were not updated in the most recent interval. Memory bool } type Option interface { applyProcessor(*config) } // WithMemory sets the memory behavior of a Processor. If this is // true, the processor will report metric instruments and label sets // that were previously reported but not updated in the most recent // interval. func WithMemory(memory bool) Option { return memoryOption(memory) } type memoryOption bool func (m memoryOption) applyProcessor(cfg *config) { cfg.Memory = bool(m) } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/processortest/000077500000000000000000000000001414226744000256215ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/processor/processortest/test.go000066400000000000000000000323731414226744000271370ustar00rootroot00000000000000// 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 processortest // import "go.opentelemetry.io/otel/sdk/metric/processor/processortest" import ( "context" "fmt" "strings" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/resource" ) type ( // mapKey is the unique key for a metric, consisting of its // unique descriptor, distinct labels, and distinct resource // attributes. mapKey struct { desc *sdkapi.Descriptor labels attribute.Distinct resource attribute.Distinct } // mapValue is value stored in a processor used to produce a // Reader. mapValue struct { labels *attribute.Set resource *resource.Resource aggregator export.Aggregator } // Output implements export.Reader. Output struct { m map[mapKey]mapValue labelEncoder attribute.Encoder sync.RWMutex } // testAggregatorSelector returns aggregators consistent with // the test variables below, needed for testing stateful // processors, which clone Aggregators using AggregatorFor(desc). testAggregatorSelector struct{} // testCheckpointer is a export.Checkpointer. testCheckpointer struct { started int finished int *Processor } // Processor is a testing implementation of export.Processor that // assembles its results as a map[string]float64. Processor struct { export.AggregatorSelector output *Output } // Exporter is a testing implementation of export.Exporter that // assembles its results as a map[string]float64. Exporter struct { aggregation.TemporalitySelector output *Output exportCount int // InjectErr supports returning conditional errors from // the Export() routine. This must be set before the // Exporter is first used. InjectErr func(export.Record) error } ) type testFactory struct { selector export.AggregatorSelector encoder attribute.Encoder } func NewCheckpointerFactory(selector export.AggregatorSelector, encoder attribute.Encoder) export.CheckpointerFactory { return testFactory{ selector: selector, encoder: encoder, } } func NewCheckpointer(p *Processor) export.Checkpointer { return &testCheckpointer{ Processor: p, } } func (f testFactory) NewCheckpointer() export.Checkpointer { return NewCheckpointer(NewProcessor(f.selector, f.encoder)) } // NewProcessor returns a new testing Processor implementation. // Verify expected outputs using Values(), e.g.: // // require.EqualValues(t, map[string]float64{ // "counter.sum/A=1,B=2/R=V": 100, // }, processor.Values()) // // Where in the example A=1,B=2 is the encoded labels and R=V is the // encoded resource value. func NewProcessor(selector export.AggregatorSelector, encoder attribute.Encoder) *Processor { return &Processor{ AggregatorSelector: selector, output: NewOutput(encoder), } } // Process implements export.Processor. func (p *Processor) Process(accum export.Accumulation) error { return p.output.AddAccumulation(accum) } // Values returns the mapping from label set to point values for the // accumulations that were processed. Point values are chosen as // either the Sum or the LastValue, whichever is implemented. (All // the built-in Aggregators implement one of these interfaces.) func (p *Processor) Values() map[string]float64 { return p.output.Map() } // Reset clears the state of this test processor. func (p *Processor) Reset() { p.output.Reset() } // StartCollection implements export.Checkpointer. func (c *testCheckpointer) StartCollection() { if c.started != c.finished { panic(fmt.Sprintf("collection was already started: %d != %d", c.started, c.finished)) } c.started++ } // FinishCollection implements export.Checkpointer. func (c *testCheckpointer) FinishCollection() error { if c.started-1 != c.finished { return fmt.Errorf("collection was not started: %d != %d", c.started, c.finished) } c.finished++ return nil } // Reader implements export.Checkpointer. func (c *testCheckpointer) Reader() export.Reader { return c.Processor.output } // AggregatorSelector returns a policy that is consistent with the // test descriptors above. I.e., it returns sum.New() for counter // instruments and lastvalue.New() for lastValue instruments. func AggregatorSelector() export.AggregatorSelector { return testAggregatorSelector{} } // AggregatorFor implements export.AggregatorSelector. func (testAggregatorSelector) AggregatorFor(desc *sdkapi.Descriptor, aggPtrs ...*export.Aggregator) { switch { case strings.HasSuffix(desc.Name(), ".disabled"): for i := range aggPtrs { *aggPtrs[i] = nil } case strings.HasSuffix(desc.Name(), ".sum"): aggs := sum.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } case strings.HasSuffix(desc.Name(), ".minmaxsumcount"): aggs := minmaxsumcount.New(len(aggPtrs), desc) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } case strings.HasSuffix(desc.Name(), ".lastvalue"): aggs := lastvalue.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } case strings.HasSuffix(desc.Name(), ".histogram"): aggs := histogram.New(len(aggPtrs), desc) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } case strings.HasSuffix(desc.Name(), ".exact"): aggs := exact.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } default: panic(fmt.Sprint("Invalid instrument name for test AggregatorSelector: ", desc.Name())) } } // NewOutput is a helper for testing an expected set of Accumulations // (from an Accumulator) or an expected set of Records (from a // Processor). If testing with an Accumulator, it may be simpler to // use the test Processor in this package. func NewOutput(labelEncoder attribute.Encoder) *Output { return &Output{ m: make(map[mapKey]mapValue), labelEncoder: labelEncoder, } } // ForEach implements export.Reader. func (o *Output) ForEach(_ aggregation.TemporalitySelector, ff func(export.Record) error) error { for key, value := range o.m { if err := ff(export.NewRecord( key.desc, value.labels, value.aggregator.Aggregation(), time.Time{}, time.Time{}, )); err != nil { return err } } return nil } // AddRecord adds a string representation of the exported metric data // to a map for use in testing. The value taken from the record is // either the Sum() or the LastValue() of its Aggregation(), whichever // is defined. Record timestamps are ignored. func (o *Output) AddRecord(rec export.Record) error { return o.AddRecordWithResource(rec, resource.Empty()) } func (o *Output) AddInstrumentationLibraryRecord(_ instrumentation.Library, rec export.Record) error { return o.AddRecordWithResource(rec, resource.Empty()) } func (o *Output) AddRecordWithResource(rec export.Record, res *resource.Resource) error { key := mapKey{ desc: rec.Descriptor(), labels: rec.Labels().Equivalent(), resource: res.Equivalent(), } if _, ok := o.m[key]; !ok { var agg export.Aggregator testAggregatorSelector{}.AggregatorFor(rec.Descriptor(), &agg) o.m[key] = mapValue{ aggregator: agg, labels: rec.Labels(), resource: res, } } return o.m[key].aggregator.Merge(rec.Aggregation().(export.Aggregator), rec.Descriptor()) } // Map returns the calculated values for test validation from a set of // Accumulations or a set of Records. When mapping records or // accumulations into floating point values, the Sum() or LastValue() // is chosen, whichever is implemented by the underlying Aggregator. func (o *Output) Map() map[string]float64 { r := make(map[string]float64) err := o.ForEach(aggregation.StatelessTemporalitySelector(), func(record export.Record) error { for key, entry := range o.m { encoded := entry.labels.Encoded(o.labelEncoder) rencoded := entry.resource.Encoded(o.labelEncoder) value := 0.0 if s, ok := entry.aggregator.(aggregation.Sum); ok { sum, _ := s.Sum() value = sum.CoerceToFloat64(key.desc.NumberKind()) } else if l, ok := entry.aggregator.(aggregation.LastValue); ok { last, _, _ := l.LastValue() value = last.CoerceToFloat64(key.desc.NumberKind()) } else if l, ok := entry.aggregator.(aggregation.Points); ok { pts, _ := l.Points() var sum number.Number for _, s := range pts { sum.AddNumber(key.desc.NumberKind(), s.Number) } value = sum.CoerceToFloat64(key.desc.NumberKind()) } else { panic(fmt.Sprintf("Unhandled aggregator type: %T", entry.aggregator)) } name := fmt.Sprint(key.desc.Name(), "/", encoded, "/", rencoded) r[name] = value } return nil }) if err != nil { panic(fmt.Sprint("Unexpected processor error: ", err)) } return r } // Reset restores the Output to its initial state, with no accumulated // metric data. func (o *Output) Reset() { o.m = map[mapKey]mapValue{} } // AddAccumulation adds a string representation of the exported metric // data to a map for use in testing. The value taken from the // accumulation is either the Sum() or the LastValue() of its // Aggregator().Aggregation(), whichever is defined. func (o *Output) AddAccumulation(acc export.Accumulation) error { return o.AddRecord( export.NewRecord( acc.Descriptor(), acc.Labels(), acc.Aggregator().Aggregation(), time.Time{}, time.Time{}, ), ) } // New returns a new testing Exporter implementation. // Verify exporter outputs using Values(), e.g.,: // // require.EqualValues(t, map[string]float64{ // "counter.sum/A=1,B=2/R=V": 100, // }, exporter.Values()) // // Where in the example A=1,B=2 is the encoded labels and R=V is the // encoded resource value. func New(selector aggregation.TemporalitySelector, encoder attribute.Encoder) *Exporter { return &Exporter{ TemporalitySelector: selector, output: NewOutput(encoder), } } func (e *Exporter) Export(_ context.Context, res *resource.Resource, ckpt export.InstrumentationLibraryReader) error { e.output.Lock() defer e.output.Unlock() e.exportCount++ return ckpt.ForEach(func(library instrumentation.Library, mr export.Reader) error { return mr.ForEach(e.TemporalitySelector, func(r export.Record) error { if e.InjectErr != nil { if err := e.InjectErr(r); err != nil { return err } } return e.output.AddRecordWithResource(r, res) }) }) } // Values returns the mapping from label set to point values for the // accumulations that were processed. Point values are chosen as // either the Sum or the LastValue, whichever is implemented. (All // the built-in Aggregators implement one of these interfaces.) func (e *Exporter) Values() map[string]float64 { e.output.Lock() defer e.output.Unlock() return e.output.Map() } // ExportCount returns the number of times Export() has been called // since the last Reset(). func (e *Exporter) ExportCount() int { e.output.Lock() defer e.output.Unlock() return e.exportCount } // Reset sets the exporter's output to the initial, empty state and // resets the export count to zero. func (e *Exporter) Reset() { e.output.Lock() defer e.output.Unlock() e.output.Reset() e.exportCount = 0 } func OneInstrumentationLibraryReader(l instrumentation.Library, r export.Reader) export.InstrumentationLibraryReader { return oneLibraryReader{l, r} } type oneLibraryReader struct { library instrumentation.Library reader export.Reader } func (o oneLibraryReader) ForEach(readerFunc func(instrumentation.Library, export.Reader) error) error { return readerFunc(o.library, o.reader) } func MultiInstrumentationLibraryReader(records map[instrumentation.Library][]export.Record) export.InstrumentationLibraryReader { return instrumentationLibraryReader{records: records} } type instrumentationLibraryReader struct { records map[instrumentation.Library][]export.Record } var _ export.InstrumentationLibraryReader = instrumentationLibraryReader{} func (m instrumentationLibraryReader) ForEach(fn func(instrumentation.Library, export.Reader) error) error { for library, records := range m.records { if err := fn(library, &metricReader{records: records}); err != nil { return err } } return nil } type metricReader struct { sync.RWMutex records []export.Record } var _ export.Reader = &metricReader{} func (m *metricReader) ForEach(_ aggregation.TemporalitySelector, fn func(export.Record) error) error { for _, record := range m.records { if err := fn(record); err != nil && err != aggregation.ErrNoData { return err } } return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/processortest/test_test.go000066400000000000000000000052251414226744000301720ustar00rootroot00000000000000// 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 processortest_test import ( "context" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" metricsdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/resource" ) func generateTestData(proc export.Processor) { ctx := context.Background() accum := metricsdk.NewAccumulator(proc) meter := metric.WrapMeterImpl(accum) counter := metric.Must(meter).NewFloat64Counter("counter.sum") _ = metric.Must(meter).NewInt64CounterObserver("observer.sum", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(10, attribute.String("K1", "V1")) result.Observe(11, attribute.String("K1", "V2")) }, ) counter.Add(ctx, 100, attribute.String("K1", "V1")) counter.Add(ctx, 101, attribute.String("K1", "V2")) accum.Collect(ctx) } func TestProcessorTesting(t *testing.T) { // Test the Processor test helper using a real Accumulator to // generate Accumulations. checkpointer := processorTest.NewCheckpointer( processorTest.NewProcessor( processorTest.AggregatorSelector(), attribute.DefaultEncoder(), ), ) generateTestData(checkpointer) res := resource.NewSchemaless(attribute.String("R", "V")) expect := map[string]float64{ "counter.sum/K1=V1/R=V": 100, "counter.sum/K1=V2/R=V": 101, "observer.sum/K1=V1/R=V": 10, "observer.sum/K1=V2/R=V": 11, } // Export the data and validate it again. exporter := processorTest.New( aggregation.StatelessTemporalitySelector(), attribute.DefaultEncoder(), ) err := exporter.Export(context.Background(), res, processortest.OneInstrumentationLibraryReader( instrumentation.Library{ Name: "test", }, checkpointer.Reader(), )) require.NoError(t, err) require.EqualValues(t, expect, exporter.Values()) } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/reducer/000077500000000000000000000000001414226744000243335ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/processor/reducer/doc.go000066400000000000000000000042331414226744000254310ustar00rootroot00000000000000// 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 reducer implements a metrics Processor component to reduce labels. This package is currently in a pre-GA phase. Backwards incompatible changes may be introduced in subsequent minor version releases as we work to track the evolving OpenTelemetry specification and user feedback. The metrics Processor component this package implements applies a `attribute.Filter` to each processed `export.Accumulation` to remove labels before passing the result to another Processor. This Processor can be used to reduce inherent dimensionality in the data, as a way to control the cost of collecting high cardinality metric data. For example, to compose a push controller with a reducer and a basic metric processor: type someFilter struct{ // configuration for this filter // ... } func (someFilter) LabelFilterFor(_ *sdkapi.Descriptor) attribute.Filter { return func(label kv.KeyValue) bool { // return true to keep this label, false to drop this label // ... } } func setupMetrics(exporter export.Exporter) (stop func()) { basicProcessor := basic.New( simple.NewWithExactDistribution(), exporter, ) reducerProcessor := reducer.New(someFilter{...}, basicProcessor) pusher := push.New( reducerProcessor, exporter, pushOpts..., ) pusher.Start() global.SetMeterProvider(pusher.Provider()) return pusher.Stop */ package reducer // import "go.opentelemetry.io/otel/sdk/metric/processor/reducer" golang-opentelemetry-otel-1.1.0/sdk/metric/processor/reducer/reducer.go000066400000000000000000000037621414226744000263230ustar00rootroot00000000000000// 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 reducer // import "go.opentelemetry.io/otel/sdk/metric/processor/reducer" import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" ) type ( // Processor implements "dimensionality reduction" by // filtering keys from export label sets. Processor struct { export.Checkpointer filterSelector LabelFilterSelector } // LabelFilterSelector is the interface used to configure a // specific Filter to an instrument. LabelFilterSelector interface { LabelFilterFor(descriptor *sdkapi.Descriptor) attribute.Filter } ) var _ export.Processor = &Processor{} var _ export.Checkpointer = &Processor{} // New returns a dimensionality-reducing Processor that passes data to // the next stage in an export pipeline. func New(filterSelector LabelFilterSelector, ckpter export.Checkpointer) *Processor { return &Processor{ Checkpointer: ckpter, filterSelector: filterSelector, } } // Process implements export.Processor. func (p *Processor) Process(accum export.Accumulation) error { // Note: the removed labels are returned and ignored here. // Conceivably these inputs could be useful to a sampler. reduced, _ := accum.Labels().Filter( p.filterSelector.LabelFilterFor( accum.Descriptor(), ), ) return p.Checkpointer.Process( export.NewAccumulation( accum.Descriptor(), &reduced, accum.Aggregator(), ), ) } golang-opentelemetry-otel-1.1.0/sdk/metric/processor/reducer/reducer_test.go000066400000000000000000000066021414226744000273560ustar00rootroot00000000000000// 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 reducer_test import ( "context" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/instrumentation" metricsdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/reducer" "go.opentelemetry.io/otel/sdk/resource" ) var ( kvs1 = []attribute.KeyValue{ attribute.Int("A", 1), attribute.Int("B", 2), attribute.Int("C", 3), } kvs2 = []attribute.KeyValue{ attribute.Int("A", 1), attribute.Int("B", 0), attribute.Int("C", 3), } ) type testFilter struct{} func (testFilter) LabelFilterFor(_ *sdkapi.Descriptor) attribute.Filter { return func(label attribute.KeyValue) bool { return label.Key == "A" || label.Key == "C" } } func generateData(impl sdkapi.MeterImpl) { ctx := context.Background() meter := metric.WrapMeterImpl(impl) counter := metric.Must(meter).NewFloat64Counter("counter.sum") _ = metric.Must(meter).NewInt64CounterObserver("observer.sum", func(_ context.Context, result metric.Int64ObserverResult) { result.Observe(10, kvs1...) result.Observe(10, kvs2...) }, ) counter.Add(ctx, 100, kvs1...) counter.Add(ctx, 100, kvs2...) } func TestFilterProcessor(t *testing.T) { testProc := processorTest.NewProcessor( processorTest.AggregatorSelector(), attribute.DefaultEncoder(), ) accum := metricsdk.NewAccumulator( reducer.New(testFilter{}, processorTest.NewCheckpointer(testProc)), ) generateData(accum) accum.Collect(context.Background()) require.EqualValues(t, map[string]float64{ "counter.sum/A=1,C=3/": 200, "observer.sum/A=1,C=3/": 20, }, testProc.Values()) } // Test a filter with the ../basic Processor. func TestFilterBasicProcessor(t *testing.T) { basicProc := basic.New(processorTest.AggregatorSelector(), aggregation.CumulativeTemporalitySelector()) accum := metricsdk.NewAccumulator( reducer.New(testFilter{}, basicProc), ) exporter := processorTest.New(basicProc, attribute.DefaultEncoder()) generateData(accum) basicProc.StartCollection() accum.Collect(context.Background()) if err := basicProc.FinishCollection(); err != nil { t.Error(err) } res := resource.NewSchemaless(attribute.String("R", "V")) require.NoError(t, exporter.Export(context.Background(), res, processortest.OneInstrumentationLibraryReader(instrumentation.Library{ Name: "test", }, basicProc.Reader()))) require.EqualValues(t, map[string]float64{ "counter.sum/A=1,C=3/R=V": 200, "observer.sum/A=1,C=3/R=V": 20, }, exporter.Values()) } golang-opentelemetry-otel-1.1.0/sdk/metric/refcount_mapped.go000066400000000000000000000040511414226744000243650ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/sdk/metric" import ( "sync/atomic" ) // refcountMapped atomically counts the number of references (usages) of an entry // while also keeping a state of mapped/unmapped into a different data structure // (an external map or list for example). // // refcountMapped uses an atomic value where the least significant bit is used to // keep the state of mapping ('1' is used for unmapped and '0' is for mapped) and // the rest of the bits are used for refcounting. type refcountMapped struct { // refcount has to be aligned for 64-bit atomic operations. value int64 } // ref returns true if the entry is still mapped and increases the // reference usages, if unmapped returns false. func (rm *refcountMapped) ref() bool { // Check if this entry was marked as unmapped between the moment // we got a reference to it (or will be removed very soon) and here. return atomic.AddInt64(&rm.value, 2)&1 == 0 } func (rm *refcountMapped) unref() { atomic.AddInt64(&rm.value, -2) } // tryUnmap flips the mapped bit to "unmapped" state and returns true if both of the // following conditions are true upon entry to this function: // * There are no active references; // * The mapped bit is in "mapped" state. // Otherwise no changes are done to mapped bit and false is returned. func (rm *refcountMapped) tryUnmap() bool { if atomic.LoadInt64(&rm.value) != 0 { return false } return atomic.CompareAndSwapInt64( &rm.value, 0, 1, ) } golang-opentelemetry-otel-1.1.0/sdk/metric/sdk.go000066400000000000000000000400561414226744000220000ustar00rootroot00000000000000// 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 metric // import "go.opentelemetry.io/otel/sdk/metric" import ( "context" "fmt" "runtime" "sync" "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" internal "go.opentelemetry.io/otel/internal/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator" ) type ( // Accumulator implements the OpenTelemetry Meter API. The // Accumulator is bound to a single export.Processor in // `NewAccumulator()`. // // The Accumulator supports a Collect() API to gather and export // current data. Collect() should be arranged according to // the processor model. Push-based processors will setup a // timer to call Collect() periodically. Pull-based processors // will call Collect() when a pull request arrives. Accumulator struct { // current maps `mapkey` to *record. current sync.Map // asyncInstruments is a set of // `*asyncInstrument` instances asyncLock sync.Mutex asyncInstruments *internal.AsyncInstrumentState // currentEpoch is the current epoch number. It is // incremented in `Collect()`. currentEpoch int64 // processor is the configured processor+configuration. processor export.Processor // collectLock prevents simultaneous calls to Collect(). collectLock sync.Mutex // asyncSortSlice has a single purpose - as a temporary // place for sorting during labels creation to avoid // allocation. It is cleared after use. asyncSortSlice attribute.Sortable } syncInstrument struct { instrument } // mapkey uniquely describes a metric instrument in terms of // its InstrumentID and the encoded form of its labels. mapkey struct { descriptor *sdkapi.Descriptor ordered attribute.Distinct } // record maintains the state of one metric instrument. Due // the use of lock-free algorithms, there may be more than one // `record` in existence at a time, although at most one can // be referenced from the `Accumulator.current` map. record struct { // refMapped keeps track of refcounts and the mapping state to the // Accumulator.current map. refMapped refcountMapped // updateCount is incremented on every Update. updateCount int64 // collectedCount is set to updateCount on collection, // supports checking for no updates during a round. collectedCount int64 // storage is the stored label set for this record, // except in cases where a label set is shared due to // batch recording. storage attribute.Set // labels is the processed label set for this record. // this may refer to the `storage` field in another // record if this label set is shared resulting from // `RecordBatch`. labels *attribute.Set // sortSlice has a single purpose - as a temporary // place for sorting during labels creation to avoid // allocation. sortSlice attribute.Sortable // inst is a pointer to the corresponding instrument. inst *syncInstrument // current implements the actual RecordOne() API, // depending on the type of aggregation. If nil, the // metric was disabled by the exporter. current export.Aggregator checkpoint export.Aggregator } instrument struct { meter *Accumulator descriptor sdkapi.Descriptor } asyncInstrument struct { instrument // recorders maps ordered labels to the pair of // labelset and recorder recorders map[attribute.Distinct]*labeledRecorder } labeledRecorder struct { observedEpoch int64 labels *attribute.Set observed export.Aggregator } ) var ( _ sdkapi.MeterImpl = &Accumulator{} _ sdkapi.AsyncImpl = &asyncInstrument{} _ sdkapi.SyncImpl = &syncInstrument{} _ sdkapi.BoundSyncImpl = &record{} // ErrUninitializedInstrument is returned when an instrument is used when uninitialized. ErrUninitializedInstrument = fmt.Errorf("use of an uninitialized instrument") ) func (inst *instrument) Descriptor() sdkapi.Descriptor { return inst.descriptor } func (a *asyncInstrument) Implementation() interface{} { return a } func (s *syncInstrument) Implementation() interface{} { return s } func (a *asyncInstrument) observe(num number.Number, labels *attribute.Set) { if err := aggregator.RangeTest(num, &a.descriptor); err != nil { otel.Handle(err) return } recorder := a.getRecorder(labels) if recorder == nil { // The instrument is disabled according to the // AggregatorSelector. return } if err := recorder.Update(context.Background(), num, &a.descriptor); err != nil { otel.Handle(err) return } } func (a *asyncInstrument) getRecorder(labels *attribute.Set) export.Aggregator { lrec, ok := a.recorders[labels.Equivalent()] if ok { // Note: SynchronizedMove(nil) can't return an error _ = lrec.observed.SynchronizedMove(nil, &a.descriptor) lrec.observedEpoch = a.meter.currentEpoch a.recorders[labels.Equivalent()] = lrec return lrec.observed } var rec export.Aggregator a.meter.processor.AggregatorFor(&a.descriptor, &rec) if a.recorders == nil { a.recorders = make(map[attribute.Distinct]*labeledRecorder) } // This may store nil recorder in the map, thus disabling the // asyncInstrument for the labelset for good. This is intentional, // but will be revisited later. a.recorders[labels.Equivalent()] = &labeledRecorder{ observed: rec, labels: labels, observedEpoch: a.meter.currentEpoch, } return rec } // acquireHandle gets or creates a `*record` corresponding to `kvs`, // the input labels. The second argument `labels` is passed in to // support re-use of the orderedLabels computed by a previous // measurement in the same batch. This performs two allocations // in the common case. func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attribute.Set) *record { var rec *record var equiv attribute.Distinct if labelPtr == nil { // This memory allocation may not be used, but it's // needed for the `sortSlice` field, to avoid an // allocation while sorting. rec = &record{} rec.storage = attribute.NewSetWithSortable(kvs, &rec.sortSlice) rec.labels = &rec.storage equiv = rec.storage.Equivalent() } else { equiv = labelPtr.Equivalent() } // Create lookup key for sync.Map (one allocation, as this // passes through an interface{}) mk := mapkey{ descriptor: &s.descriptor, ordered: equiv, } if actual, ok := s.meter.current.Load(mk); ok { // Existing record case. existingRec := actual.(*record) if existingRec.refMapped.ref() { // At this moment it is guaranteed that the entry is in // the map and will not be removed. return existingRec } // This entry is no longer mapped, try to add a new entry. } if rec == nil { rec = &record{} rec.labels = labelPtr } rec.refMapped = refcountMapped{value: 2} rec.inst = s s.meter.processor.AggregatorFor(&s.descriptor, &rec.current, &rec.checkpoint) for { // Load/Store: there's a memory allocation to place `mk` into // an interface here. if actual, loaded := s.meter.current.LoadOrStore(mk, rec); loaded { // Existing record case. Cannot change rec here because if fail // will try to add rec again to avoid new allocations. oldRec := actual.(*record) if oldRec.refMapped.ref() { // At this moment it is guaranteed that the entry is in // the map and will not be removed. return oldRec } // This loaded entry is marked as unmapped (so Collect will remove // it from the map immediately), try again - this is a busy waiting // strategy to wait until Collect() removes this entry from the map. // // This can be improved by having a list of "Unmapped" entries for // one time only usages, OR we can make this a blocking path and use // a Mutex that protects the delete operation (delete only if the old // record is associated with the key). // Let collector get work done to remove the entry from the map. runtime.Gosched() continue } // The new entry was added to the map, good to go. return rec } } // The order of the input array `kvs` may be sorted after the function is called. func (s *syncInstrument) Bind(kvs []attribute.KeyValue) sdkapi.BoundSyncImpl { return s.acquireHandle(kvs, nil) } // The order of the input array `kvs` may be sorted after the function is called. func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs []attribute.KeyValue) { h := s.acquireHandle(kvs, nil) defer h.Unbind() h.RecordOne(ctx, num) } // NewAccumulator constructs a new Accumulator for the given // processor. This Accumulator supports only a single processor. // // The Accumulator does not start any background process to collect itself // periodically, this responsibility lies with the processor, typically, // depending on the type of export. For example, a pull-based // processor will call Collect() when it receives a request to scrape // current metric values. A push-based processor should configure its // own periodic collection. func NewAccumulator(processor export.Processor) *Accumulator { return &Accumulator{ processor: processor, asyncInstruments: internal.NewAsyncInstrumentState(), } } // NewSyncInstrument implements sdkapi.MetricImpl. func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) { return &syncInstrument{ instrument: instrument{ descriptor: descriptor, meter: m, }, }, nil } // NewAsyncInstrument implements sdkapi.MetricImpl. func (m *Accumulator) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) { a := &asyncInstrument{ instrument: instrument{ descriptor: descriptor, meter: m, }, } m.asyncLock.Lock() defer m.asyncLock.Unlock() m.asyncInstruments.Register(a, runner) return a, nil } // Collect traverses the list of active records and observers and // exports data for each active instrument. Collect() may not be // called concurrently. // // During the collection pass, the export.Processor will receive // one Export() call per current aggregation. // // Returns the number of records that were checkpointed. func (m *Accumulator) Collect(ctx context.Context) int { m.collectLock.Lock() defer m.collectLock.Unlock() checkpointed := m.observeAsyncInstruments(ctx) checkpointed += m.collectSyncInstruments() m.currentEpoch++ return checkpointed } func (m *Accumulator) collectSyncInstruments() int { checkpointed := 0 m.current.Range(func(key interface{}, value interface{}) bool { // Note: always continue to iterate over the entire // map by returning `true` in this function. inuse := value.(*record) mods := atomic.LoadInt64(&inuse.updateCount) coll := inuse.collectedCount if mods != coll { // Updates happened in this interval, // checkpoint and continue. checkpointed += m.checkpointRecord(inuse) inuse.collectedCount = mods return true } // Having no updates since last collection, try to unmap: if unmapped := inuse.refMapped.tryUnmap(); !unmapped { // The record is referenced by a binding, continue. return true } // If any other goroutines are now trying to re-insert this // entry in the map, they are busy calling Gosched() awaiting // this deletion: m.current.Delete(inuse.mapkey()) // There's a potential race between `LoadInt64` and // `tryUnmap` in this function. Since this is the // last we'll see of this record, checkpoint mods = atomic.LoadInt64(&inuse.updateCount) if mods != coll { checkpointed += m.checkpointRecord(inuse) } return true }) return checkpointed } // CollectAsync implements internal.AsyncCollector. // The order of the input array `kvs` may be sorted after the function is called. func (m *Accumulator) CollectAsync(kv []attribute.KeyValue, obs ...sdkapi.Observation) { labels := attribute.NewSetWithSortable(kv, &m.asyncSortSlice) for _, ob := range obs { if a := m.fromAsync(ob.AsyncImpl()); a != nil { a.observe(ob.Number(), &labels) } } } func (m *Accumulator) observeAsyncInstruments(ctx context.Context) int { m.asyncLock.Lock() defer m.asyncLock.Unlock() asyncCollected := 0 m.asyncInstruments.Run(ctx, m) for _, inst := range m.asyncInstruments.Instruments() { if a := m.fromAsync(inst); a != nil { asyncCollected += m.checkpointAsync(a) } } return asyncCollected } func (m *Accumulator) checkpointRecord(r *record) int { if r.current == nil { return 0 } err := r.current.SynchronizedMove(r.checkpoint, &r.inst.descriptor) if err != nil { otel.Handle(err) return 0 } a := export.NewAccumulation(&r.inst.descriptor, r.labels, r.checkpoint) err = m.processor.Process(a) if err != nil { otel.Handle(err) } return 1 } func (m *Accumulator) checkpointAsync(a *asyncInstrument) int { if len(a.recorders) == 0 { return 0 } checkpointed := 0 for encodedLabels, lrec := range a.recorders { lrec := lrec epochDiff := m.currentEpoch - lrec.observedEpoch if epochDiff == 0 { if lrec.observed != nil { a := export.NewAccumulation(&a.descriptor, lrec.labels, lrec.observed) err := m.processor.Process(a) if err != nil { otel.Handle(err) } checkpointed++ } } else if epochDiff > 1 { // This is second collection cycle with no // observations for this labelset. Remove the // recorder. delete(a.recorders, encodedLabels) } } if len(a.recorders) == 0 { a.recorders = nil } return checkpointed } // RecordBatch enters a batch of metric events. // The order of the input array `kvs` may be sorted after the function is called. func (m *Accumulator) RecordBatch(ctx context.Context, kvs []attribute.KeyValue, measurements ...sdkapi.Measurement) { // Labels will be computed the first time acquireHandle is // called. Subsequent calls to acquireHandle will re-use the // previously computed value instead of recomputing the // ordered labels. var labelsPtr *attribute.Set for i, meas := range measurements { s := m.fromSync(meas.SyncImpl()) if s == nil { continue } h := s.acquireHandle(kvs, labelsPtr) // Re-use labels for the next measurement. if i == 0 { labelsPtr = h.labels } defer h.Unbind() h.RecordOne(ctx, meas.Number()) } } // RecordOne implements sdkapi.SyncImpl. func (r *record) RecordOne(ctx context.Context, num number.Number) { if r.current == nil { // The instrument is disabled according to the AggregatorSelector. return } if err := aggregator.RangeTest(num, &r.inst.descriptor); err != nil { otel.Handle(err) return } if err := r.current.Update(ctx, num, &r.inst.descriptor); err != nil { otel.Handle(err) return } // Record was modified, inform the Collect() that things need // to be collected while the record is still mapped. atomic.AddInt64(&r.updateCount, 1) } // Unbind implements sdkapi.SyncImpl. func (r *record) Unbind() { r.refMapped.unref() } func (r *record) mapkey() mapkey { return mapkey{ descriptor: &r.inst.descriptor, ordered: r.labels.Equivalent(), } } // fromSync gets a sync implementation object, checking for // uninitialized instruments and instruments created by another SDK. func (m *Accumulator) fromSync(sync sdkapi.SyncImpl) *syncInstrument { if sync != nil { if inst, ok := sync.Implementation().(*syncInstrument); ok { return inst } } otel.Handle(ErrUninitializedInstrument) return nil } // fromSync gets an async implementation object, checking for // uninitialized instruments and instruments created by another SDK. func (m *Accumulator) fromAsync(async sdkapi.AsyncImpl) *asyncInstrument { if async != nil { if inst, ok := async.Implementation().(*asyncInstrument); ok { return inst } } otel.Handle(ErrUninitializedInstrument) return nil } golang-opentelemetry-otel-1.1.0/sdk/metric/selector/000077500000000000000000000000001414226744000225035ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/selector/simple/000077500000000000000000000000001414226744000237745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/metric/selector/simple/simple.go000066400000000000000000000076111414226744000256210ustar00rootroot00000000000000// 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 simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple" import ( "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" ) type ( selectorInexpensive struct{} selectorExact struct{} selectorHistogram struct { options []histogram.Option } ) var ( _ export.AggregatorSelector = selectorInexpensive{} _ export.AggregatorSelector = selectorExact{} _ export.AggregatorSelector = selectorHistogram{} ) // NewWithInexpensiveDistribution returns a simple aggregator selector // that uses minmaxsumcount aggregators for `Histogram` // instruments. This selector is faster and uses less memory than the // others in this package because minmaxsumcount aggregators maintain // the least information about the distribution among these choices. func NewWithInexpensiveDistribution() export.AggregatorSelector { return selectorInexpensive{} } // NewWithExactDistribution returns a simple aggregator selector that // uses exact aggregators for `Histogram` instruments. This // selector uses more memory than the others in this package because // exact aggregators maintain the most information about the // distribution among these choices. func NewWithExactDistribution() export.AggregatorSelector { return selectorExact{} } // NewWithHistogramDistribution returns a simple aggregator selector // that uses histogram aggregators for `Histogram` instruments. // This selector is a good default choice for most metric exporters. func NewWithHistogramDistribution(options ...histogram.Option) export.AggregatorSelector { return selectorHistogram{options: options} } func sumAggs(aggPtrs []*export.Aggregator) { aggs := sum.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } } func lastValueAggs(aggPtrs []*export.Aggregator) { aggs := lastvalue.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } } func (selectorInexpensive) AggregatorFor(descriptor *sdkapi.Descriptor, aggPtrs ...*export.Aggregator) { switch descriptor.InstrumentKind() { case sdkapi.GaugeObserverInstrumentKind: lastValueAggs(aggPtrs) case sdkapi.HistogramInstrumentKind: aggs := minmaxsumcount.New(len(aggPtrs), descriptor) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } default: sumAggs(aggPtrs) } } func (selectorExact) AggregatorFor(descriptor *sdkapi.Descriptor, aggPtrs ...*export.Aggregator) { switch descriptor.InstrumentKind() { case sdkapi.GaugeObserverInstrumentKind: lastValueAggs(aggPtrs) case sdkapi.HistogramInstrumentKind: aggs := exact.New(len(aggPtrs)) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } default: sumAggs(aggPtrs) } } func (s selectorHistogram) AggregatorFor(descriptor *sdkapi.Descriptor, aggPtrs ...*export.Aggregator) { switch descriptor.InstrumentKind() { case sdkapi.GaugeObserverInstrumentKind: lastValueAggs(aggPtrs) case sdkapi.HistogramInstrumentKind: aggs := histogram.New(len(aggPtrs), descriptor, s.options...) for i := range aggPtrs { *aggPtrs[i] = &aggs[i] } default: sumAggs(aggPtrs) } } golang-opentelemetry-otel-1.1.0/sdk/metric/selector/simple/simple_test.go000066400000000000000000000062651414226744000266640ustar00rootroot00000000000000// 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 simple_test import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/selector/simple" ) var ( testCounterDesc = metrictest.NewDescriptor("counter", sdkapi.CounterInstrumentKind, number.Int64Kind) testUpDownCounterDesc = metrictest.NewDescriptor("updowncounter", sdkapi.UpDownCounterInstrumentKind, number.Int64Kind) testCounterObserverDesc = metrictest.NewDescriptor("counterobserver", sdkapi.CounterObserverInstrumentKind, number.Int64Kind) testUpDownCounterObserverDesc = metrictest.NewDescriptor("updowncounterobserver", sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind) testHistogramDesc = metrictest.NewDescriptor("histogram", sdkapi.HistogramInstrumentKind, number.Int64Kind) testGaugeObserverDesc = metrictest.NewDescriptor("gauge", sdkapi.GaugeObserverInstrumentKind, number.Int64Kind) ) func oneAgg(sel export.AggregatorSelector, desc *sdkapi.Descriptor) export.Aggregator { var agg export.Aggregator sel.AggregatorFor(desc, &agg) return agg } func testFixedSelectors(t *testing.T, sel export.AggregatorSelector) { require.IsType(t, (*lastvalue.Aggregator)(nil), oneAgg(sel, &testGaugeObserverDesc)) require.IsType(t, (*sum.Aggregator)(nil), oneAgg(sel, &testCounterDesc)) require.IsType(t, (*sum.Aggregator)(nil), oneAgg(sel, &testUpDownCounterDesc)) require.IsType(t, (*sum.Aggregator)(nil), oneAgg(sel, &testCounterObserverDesc)) require.IsType(t, (*sum.Aggregator)(nil), oneAgg(sel, &testUpDownCounterObserverDesc)) } func TestInexpensiveDistribution(t *testing.T) { inex := simple.NewWithInexpensiveDistribution() require.IsType(t, (*minmaxsumcount.Aggregator)(nil), oneAgg(inex, &testHistogramDesc)) testFixedSelectors(t, inex) } func TestExactDistribution(t *testing.T) { ex := simple.NewWithExactDistribution() require.IsType(t, (*exact.Aggregator)(nil), oneAgg(ex, &testHistogramDesc)) testFixedSelectors(t, ex) } func TestHistogramDistribution(t *testing.T) { hist := simple.NewWithHistogramDistribution() require.IsType(t, (*histogram.Aggregator)(nil), oneAgg(hist, &testHistogramDesc)) testFixedSelectors(t, hist) } golang-opentelemetry-otel-1.1.0/sdk/metric/stress_test.go000066400000000000000000000313561414226744000236040ustar00rootroot00000000000000// 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. // This test is too large for the race detector. This SDK uses no locks // that the race detector would help with, anyway. //go:build !race // +build !race package metric import ( "context" "fmt" "math" "math/rand" "runtime" "sort" "strings" "sync" "sync/atomic" "testing" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/sdkapi" export "go.opentelemetry.io/otel/sdk/export/metric" "go.opentelemetry.io/otel/sdk/export/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" ) const ( concurrencyPerCPU = 100 reclaimPeriod = time.Millisecond * 100 testRun = 5 * time.Second epsilon = 1e-10 ) var Must = metric.Must type ( testFixture struct { // stop has to be aligned for 64-bit atomic operations. stop int64 expected sync.Map received sync.Map // Note: doesn't require synchronization wg sync.WaitGroup impl testImpl T *testing.T export.AggregatorSelector lock sync.Mutex lused map[string]bool dupCheck map[testKey]int totalDups int64 } testKey struct { labels string descriptor *sdkapi.Descriptor } testImpl struct { newInstrument func(meter metric.Meter, name string) SyncImpler getUpdateValue func() number.Number operate func(interface{}, context.Context, number.Number, []attribute.KeyValue) newStore func() interface{} // storeCollect and storeExpect are the same for // counters, different for lastValues, to ensure we are // testing the timestamps correctly. storeCollect func(store interface{}, value number.Number, ts time.Time) storeExpect func(store interface{}, value number.Number) readStore func(store interface{}) number.Number equalValues func(a, b number.Number) bool } SyncImpler interface { SyncImpl() sdkapi.SyncImpl } // lastValueState supports merging lastValue values, for the case // where a race condition causes duplicate records. We always // take the later timestamp. lastValueState struct { // raw has to be aligned for 64-bit atomic operations. raw number.Number ts time.Time } ) func concurrency() int { return concurrencyPerCPU * runtime.NumCPU() } func canonicalizeLabels(ls []attribute.KeyValue) string { copy := append(ls[0:0:0], ls...) sort.SliceStable(copy, func(i, j int) bool { return copy[i].Key < copy[j].Key }) var b strings.Builder for _, kv := range copy { b.WriteString(string(kv.Key)) b.WriteString("=") b.WriteString(kv.Value.Emit()) b.WriteString("$") } return b.String() } func getPeriod() time.Duration { dur := math.Max( float64(reclaimPeriod)/10, float64(reclaimPeriod)*(1+0.1*rand.NormFloat64()), ) return time.Duration(dur) } func (f *testFixture) someLabels() []attribute.KeyValue { n := 1 + rand.Intn(3) l := make([]attribute.KeyValue, n) for { oused := map[string]bool{} for i := 0; i < n; i++ { var k string for { k = fmt.Sprint("k", rand.Intn(1000000000)) if !oused[k] { oused[k] = true break } } l[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000))) } lc := canonicalizeLabels(l) f.lock.Lock() avail := !f.lused[lc] if avail { f.lused[lc] = true f.lock.Unlock() return l } f.lock.Unlock() } } func (f *testFixture) startWorker(impl *Accumulator, meter metric.Meter, wg *sync.WaitGroup, i int) { ctx := context.Background() name := fmt.Sprint("test_", i) instrument := f.impl.newInstrument(meter, name) var descriptor *sdkapi.Descriptor if ii, ok := instrument.SyncImpl().(*syncInstrument); ok { descriptor = &ii.descriptor } kvs := f.someLabels() clabs := canonicalizeLabels(kvs) dur := getPeriod() key := testKey{ labels: clabs, descriptor: descriptor, } for { sleep := time.Duration(rand.ExpFloat64() * float64(dur)) time.Sleep(sleep) value := f.impl.getUpdateValue() f.impl.operate(instrument, ctx, value, kvs) actual, _ := f.expected.LoadOrStore(key, f.impl.newStore()) f.impl.storeExpect(actual, value) if atomic.LoadInt64(&f.stop) != 0 { wg.Done() return } } } func (f *testFixture) assertTest(numCollect int) { var allErrs []func() csize := 0 f.received.Range(func(key, gstore interface{}) bool { csize++ gvalue := f.impl.readStore(gstore) estore, loaded := f.expected.Load(key) if !loaded { allErrs = append(allErrs, func() { f.T.Error("Could not locate expected key: ", key) }) return true } evalue := f.impl.readStore(estore) if !f.impl.equalValues(evalue, gvalue) { allErrs = append(allErrs, func() { f.T.Error("Expected value mismatch: ", evalue, "!=", gvalue, " for ", key) }) } return true }) rsize := 0 f.expected.Range(func(key, value interface{}) bool { rsize++ if _, loaded := f.received.Load(key); !loaded { allErrs = append(allErrs, func() { f.T.Error("Did not receive expected key: ", key) }) } return true }) if rsize != csize { f.T.Error("Did not receive the correct set of metrics: Received != Expected", rsize, csize) } for _, anErr := range allErrs { anErr() } // Note: It's useful to know the test triggers this condition, // but we can't assert it. Infrequently no duplicates are // found, and we can't really force a race to happen. // // fmt.Printf("Test duplicate records seen: %.1f%%\n", // float64(100*f.totalDups/int64(numCollect*concurrency()))) } func (f *testFixture) preCollect() { // Collect calls Process in a single-threaded context. No need // to lock this struct. f.dupCheck = map[testKey]int{} } func (*testFixture) Reader() export.Reader { return nil } func (f *testFixture) Process(accumulation export.Accumulation) error { labels := accumulation.Labels().ToSlice() key := testKey{ labels: canonicalizeLabels(labels), descriptor: accumulation.Descriptor(), } if f.dupCheck[key] == 0 { f.dupCheck[key]++ } else { f.totalDups++ } actual, _ := f.received.LoadOrStore(key, f.impl.newStore()) agg := accumulation.Aggregator() switch accumulation.Descriptor().InstrumentKind() { case sdkapi.CounterInstrumentKind: sum, err := agg.(aggregation.Sum).Sum() if err != nil { f.T.Fatal("Sum error: ", err) } f.impl.storeCollect(actual, sum, time.Time{}) case sdkapi.HistogramInstrumentKind: lv, ts, err := agg.(aggregation.LastValue).LastValue() if err != nil && err != aggregation.ErrNoData { f.T.Fatal("Last value error: ", err) } f.impl.storeCollect(actual, lv, ts) default: panic("Not used in this test") } return nil } func stressTest(t *testing.T, impl testImpl) { ctx := context.Background() t.Parallel() fixture := &testFixture{ T: t, impl: impl, lused: map[string]bool{}, AggregatorSelector: processortest.AggregatorSelector(), } cc := concurrency() sdk := NewAccumulator(fixture) meter := metric.WrapMeterImpl(sdk) fixture.wg.Add(cc + 1) for i := 0; i < cc; i++ { go fixture.startWorker(sdk, meter, &fixture.wg, i) } numCollect := 0 go func() { for { time.Sleep(reclaimPeriod) fixture.preCollect() sdk.Collect(ctx) numCollect++ if atomic.LoadInt64(&fixture.stop) != 0 { fixture.wg.Done() return } } }() time.Sleep(testRun) atomic.StoreInt64(&fixture.stop, 1) fixture.wg.Wait() fixture.preCollect() sdk.Collect(ctx) numCollect++ fixture.assertTest(numCollect) } func int64sEqual(a, b number.Number) bool { return a.AsInt64() == b.AsInt64() } func float64sEqual(a, b number.Number) bool { diff := math.Abs(a.AsFloat64() - b.AsFloat64()) return diff < math.Abs(a.AsFloat64())*epsilon } // Counters func intCounterTestImpl() testImpl { return testImpl{ newInstrument: func(meter metric.Meter, name string) SyncImpler { return Must(meter).NewInt64Counter(name + ".sum") }, getUpdateValue: func() number.Number { for { x := int64(rand.Intn(100)) if x != 0 { return number.NewInt64Number(x) } } }, operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) { counter := inst.(metric.Int64Counter) counter.Add(ctx, value.AsInt64(), labels...) }, newStore: func() interface{} { n := number.NewInt64Number(0) return &n }, storeCollect: func(store interface{}, value number.Number, _ time.Time) { store.(*number.Number).AddInt64Atomic(value.AsInt64()) }, storeExpect: func(store interface{}, value number.Number) { store.(*number.Number).AddInt64Atomic(value.AsInt64()) }, readStore: func(store interface{}) number.Number { return store.(*number.Number).AsNumberAtomic() }, equalValues: int64sEqual, } } func TestStressInt64Counter(t *testing.T) { stressTest(t, intCounterTestImpl()) } func floatCounterTestImpl() testImpl { return testImpl{ newInstrument: func(meter metric.Meter, name string) SyncImpler { return Must(meter).NewFloat64Counter(name + ".sum") }, getUpdateValue: func() number.Number { for { x := rand.Float64() if x != 0 { return number.NewFloat64Number(x) } } }, operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) { counter := inst.(metric.Float64Counter) counter.Add(ctx, value.AsFloat64(), labels...) }, newStore: func() interface{} { n := number.NewFloat64Number(0.0) return &n }, storeCollect: func(store interface{}, value number.Number, _ time.Time) { store.(*number.Number).AddFloat64Atomic(value.AsFloat64()) }, storeExpect: func(store interface{}, value number.Number) { store.(*number.Number).AddFloat64Atomic(value.AsFloat64()) }, readStore: func(store interface{}) number.Number { return store.(*number.Number).AsNumberAtomic() }, equalValues: float64sEqual, } } func TestStressFloat64Counter(t *testing.T) { stressTest(t, floatCounterTestImpl()) } // LastValue func intLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter metric.Meter, name string) SyncImpler { return Must(meter).NewInt64Histogram(name + ".lastvalue") }, getUpdateValue: func() number.Number { r1 := rand.Int63() return number.NewInt64Number(rand.Int63() - r1) }, operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) { histogram := inst.(metric.Int64Histogram) histogram.Record(ctx, value.AsInt64(), labels...) }, newStore: func() interface{} { return &lastValueState{ raw: number.NewInt64Number(0), } }, storeCollect: func(store interface{}, value number.Number, ts time.Time) { gs := store.(*lastValueState) if !ts.Before(gs.ts) { gs.ts = ts gs.raw.SetInt64Atomic(value.AsInt64()) } }, storeExpect: func(store interface{}, value number.Number) { gs := store.(*lastValueState) gs.raw.SetInt64Atomic(value.AsInt64()) }, readStore: func(store interface{}) number.Number { gs := store.(*lastValueState) return gs.raw.AsNumberAtomic() }, equalValues: int64sEqual, } } func TestStressInt64LastValue(t *testing.T) { stressTest(t, intLastValueTestImpl()) } func floatLastValueTestImpl() testImpl { return testImpl{ newInstrument: func(meter metric.Meter, name string) SyncImpler { return Must(meter).NewFloat64Histogram(name + ".lastvalue") }, getUpdateValue: func() number.Number { return number.NewFloat64Number((-0.5 + rand.Float64()) * 100000) }, operate: func(inst interface{}, ctx context.Context, value number.Number, labels []attribute.KeyValue) { histogram := inst.(metric.Float64Histogram) histogram.Record(ctx, value.AsFloat64(), labels...) }, newStore: func() interface{} { return &lastValueState{ raw: number.NewFloat64Number(0), } }, storeCollect: func(store interface{}, value number.Number, ts time.Time) { gs := store.(*lastValueState) if !ts.Before(gs.ts) { gs.ts = ts gs.raw.SetFloat64Atomic(value.AsFloat64()) } }, storeExpect: func(store interface{}, value number.Number) { gs := store.(*lastValueState) gs.raw.SetFloat64Atomic(value.AsFloat64()) }, readStore: func(store interface{}) number.Number { gs := store.(*lastValueState) return gs.raw.AsNumberAtomic() }, equalValues: float64sEqual, } } func TestStressFloat64LastValue(t *testing.T) { stressTest(t, floatLastValueTestImpl()) } golang-opentelemetry-otel-1.1.0/sdk/resource/000077500000000000000000000000001414226744000212275ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/resource/auto.go000066400000000000000000000046541414226744000225370ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "errors" "fmt" ) var ( // ErrPartialResource is returned by a detector when complete source // information for a Resource is unavailable or the source information // contains invalid values that are omitted from the returned Resource. ErrPartialResource = errors.New("partial resource") ) // Detector detects OpenTelemetry resource information type Detector interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Detect returns an initialized Resource based on gathered information. // If the source information to construct a Resource contains invalid // values, a Resource is returned with the valid parts of the source // information used for initialization along with an appropriately // wrapped ErrPartialResource error. Detect(ctx context.Context) (*Resource, error) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // Detect calls all input detectors sequentially and merges each result with the previous one. // It returns the merged error too. func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { var autoDetectedRes *Resource var errInfo []string for _, detector := range detectors { if detector == nil { continue } res, err := detector.Detect(ctx) if err != nil { errInfo = append(errInfo, err.Error()) if !errors.Is(err, ErrPartialResource) { continue } } autoDetectedRes, err = Merge(autoDetectedRes, res) if err != nil { errInfo = append(errInfo, err.Error()) } } var aggregatedError error if len(errInfo) > 0 { aggregatedError = fmt.Errorf("detecting resources: %s", errInfo) } return autoDetectedRes, aggregatedError } golang-opentelemetry-otel-1.1.0/sdk/resource/auto_test.go000066400000000000000000000032421414226744000235660ustar00rootroot00000000000000// 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 resource_test import ( "context" "fmt" "os" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) func TestDetect(t *testing.T) { cases := []struct { name string schema1, schema2 string isErr bool }{ { name: "different schema urls", schema1: "https://opentelemetry.io/schemas/1.3.0", schema2: "https://opentelemetry.io/schemas/1.4.0", isErr: true, }, { name: "same schema url", schema1: "https://opentelemetry.io/schemas/1.4.0", schema2: "https://opentelemetry.io/schemas/1.4.0", isErr: false, }, } for _, c := range cases { t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { d1 := resource.StringDetector(c.schema1, semconv.HostNameKey, os.Hostname) d2 := resource.StringDetector(c.schema2, semconv.HostNameKey, os.Hostname) r, err := resource.Detect(context.Background(), d1, d2) assert.NotNil(t, r) if c.isErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } golang-opentelemetry-otel-1.1.0/sdk/resource/benchmark_test.go000066400000000000000000000040431414226744000245500ustar00rootroot00000000000000// 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 resource_test import ( "fmt" "math/rand" "testing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" ) const conflict = 0.5 func makeLabels(n int) (_, _ *resource.Resource) { used := map[string]bool{} l1 := make([]attribute.KeyValue, n) l2 := make([]attribute.KeyValue, n) for i := 0; i < n; i++ { var k string for { k = fmt.Sprint("k", rand.Intn(1000000000)) if !used[k] { used[k] = true break } } l1[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000))) if rand.Float64() < conflict { l2[i] = l1[i] } else { l2[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000))) } } return resource.NewSchemaless(l1...), resource.NewSchemaless(l2...) } func benchmarkMergeResource(b *testing.B, size int) { r1, r2 := makeLabels(size) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = resource.Merge(r1, r2) } } func BenchmarkMergeResource_1(b *testing.B) { benchmarkMergeResource(b, 1) } func BenchmarkMergeResource_2(b *testing.B) { benchmarkMergeResource(b, 2) } func BenchmarkMergeResource_3(b *testing.B) { benchmarkMergeResource(b, 3) } func BenchmarkMergeResource_4(b *testing.B) { benchmarkMergeResource(b, 4) } func BenchmarkMergeResource_6(b *testing.B) { benchmarkMergeResource(b, 6) } func BenchmarkMergeResource_8(b *testing.B) { benchmarkMergeResource(b, 8) } func BenchmarkMergeResource_16(b *testing.B) { benchmarkMergeResource(b, 16) } golang-opentelemetry-otel-1.1.0/sdk/resource/builtin.go000066400000000000000000000066631414226744000232370ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "fmt" "os" "path/filepath" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) type ( // telemetrySDK is a Detector that provides information about // the OpenTelemetry SDK used. This Detector is included as a // builtin. If these resource attributes are not wanted, use // the WithTelemetrySDK(nil) or WithoutBuiltin() options to // explicitly disable them. telemetrySDK struct{} // host is a Detector that provides information about the host // being run on. This Detector is included as a builtin. If // these resource attributes are not wanted, use the // WithHost(nil) or WithoutBuiltin() options to explicitly // disable them. host struct{} stringDetector struct { schemaURL string K attribute.Key F func() (string, error) } defaultServiceNameDetector struct{} ) var ( _ Detector = telemetrySDK{} _ Detector = host{} _ Detector = stringDetector{} _ Detector = defaultServiceNameDetector{} ) // Detect returns a *Resource that describes the OpenTelemetry SDK used. func (telemetrySDK) Detect(context.Context) (*Resource, error) { return NewWithAttributes( semconv.SchemaURL, semconv.TelemetrySDKNameKey.String("opentelemetry"), semconv.TelemetrySDKLanguageKey.String("go"), semconv.TelemetrySDKVersionKey.String(otel.Version()), ), nil } // Detect returns a *Resource that describes the host being run on. func (host) Detect(ctx context.Context) (*Resource, error) { return StringDetector(semconv.SchemaURL, semconv.HostNameKey, os.Hostname).Detect(ctx) } // StringDetector returns a Detector that will produce a *Resource // containing the string as a value corresponding to k. The resulting Resource // will have the specified schemaURL. func StringDetector(schemaURL string, k attribute.Key, f func() (string, error)) Detector { return stringDetector{schemaURL: schemaURL, K: k, F: f} } // Detect returns a *Resource that describes the string as a value // corresponding to attribute.Key as well as the specific schemaURL. func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) { value, err := sd.F() if err != nil { return nil, fmt.Errorf("%s: %w", string(sd.K), err) } a := sd.K.String(value) if !a.Valid() { return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.Emit()) } return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil } // Detect implements Detector func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error) { return StringDetector( semconv.SchemaURL, semconv.ServiceNameKey, func() (string, error) { executable, err := os.Executable() if err != nil { return "unknown_service:go", nil } return "unknown_service:" + filepath.Base(executable), nil }, ).Detect(ctx) } golang-opentelemetry-otel-1.1.0/sdk/resource/builtin_test.go000066400000000000000000000042151414226744000242650ustar00rootroot00000000000000// 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 resource_test import ( "context" "errors" "fmt" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" ) func TestBuiltinStringDetector(t *testing.T) { E := fmt.Errorf("no K") res, err := resource.StringDetector("", attribute.Key("K"), func() (string, error) { return "", E }).Detect(context.Background()) require.True(t, errors.Is(err, E)) require.NotEqual(t, E, err) require.Nil(t, res) } func TestStringDetectorErrors(t *testing.T) { tests := []struct { desc string s resource.Detector errContains string }{ { desc: "explicit error from func should be returned", s: resource.StringDetector("", attribute.Key("K"), func() (string, error) { return "", fmt.Errorf("K-IS-MISSING") }), errContains: "K-IS-MISSING", }, { desc: "empty key is an invalid", s: resource.StringDetector("", attribute.Key(""), func() (string, error) { return "not-empty", nil }), errContains: "invalid attribute: \"\" -> \"not-empty\"", }, } for _, test := range tests { res, err := resource.New( context.Background(), resource.WithAttributes(attribute.String("A", "B")), resource.WithDetectors(test.s), ) require.Error(t, err, test.desc) require.Contains(t, err.Error(), test.errContains) require.NotNil(t, res, "resource contains remaining valid entries") m := map[string]string{} for _, kv := range res.Attributes() { m[string(kv.Key)] = kv.Value.Emit() } require.EqualValues(t, map[string]string{"A": "B"}, m) } } golang-opentelemetry-otel-1.1.0/sdk/resource/config.go000066400000000000000000000124141414226744000230250ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "go.opentelemetry.io/otel/attribute" ) // config contains configuration for Resource creation. type config struct { // detectors that will be evaluated. detectors []Detector // SchemaURL to associate with the Resource. schemaURL string } // Option is the interface that applies a configuration option. type Option interface { // apply sets the Option value of a config. apply(*config) } // WithAttributes adds attributes to the configured Resource. func WithAttributes(attributes ...attribute.KeyValue) Option { return WithDetectors(detectAttributes{attributes}) } type detectAttributes struct { attributes []attribute.KeyValue } func (d detectAttributes) Detect(context.Context) (*Resource, error) { return NewSchemaless(d.attributes...), nil } // WithDetectors adds detectors to be evaluated for the configured resource. func WithDetectors(detectors ...Detector) Option { return detectorsOption{detectors: detectors} } type detectorsOption struct { detectors []Detector } func (o detectorsOption) apply(cfg *config) { cfg.detectors = append(cfg.detectors, o.detectors...) } // WithFromEnv adds attributes from environment variables to the configured resource. func WithFromEnv() Option { return WithDetectors(fromEnv{}) } // WithHost adds attributes from the host to the configured resource. func WithHost() Option { return WithDetectors(host{}) } // WithTelemetrySDK adds TelemetrySDK version info to the configured resource. func WithTelemetrySDK() Option { return WithDetectors(telemetrySDK{}) } // WithSchemaURL sets the schema URL for the configured resource. func WithSchemaURL(schemaURL string) Option { return schemaURLOption(schemaURL) } type schemaURLOption string func (o schemaURLOption) apply(cfg *config) { cfg.schemaURL = string(o) } // WithOS adds all the OS attributes to the configured Resource. // See individual WithOS* functions to configure specific attributes. func WithOS() Option { return WithDetectors( osTypeDetector{}, osDescriptionDetector{}, ) } // WithOSType adds an attribute with the operating system type to the configured Resource. func WithOSType() Option { return WithDetectors(osTypeDetector{}) } // WithOSDescription adds an attribute with the operating system description to the // configured Resource. The formatted string is equivalent to the output of the // `uname -snrvm` command. func WithOSDescription() Option { return WithDetectors(osDescriptionDetector{}) } // WithProcess adds all the Process attributes to the configured Resource. // See individual WithProcess* functions to configure specific attributes. func WithProcess() Option { return WithDetectors( processPIDDetector{}, processExecutableNameDetector{}, processExecutablePathDetector{}, processCommandArgsDetector{}, processOwnerDetector{}, processRuntimeNameDetector{}, processRuntimeVersionDetector{}, processRuntimeDescriptionDetector{}, ) } // WithProcessPID adds an attribute with the process identifier (PID) to the // configured Resource. func WithProcessPID() Option { return WithDetectors(processPIDDetector{}) } // WithProcessExecutableName adds an attribute with the name of the process // executable to the configured Resource. func WithProcessExecutableName() Option { return WithDetectors(processExecutableNameDetector{}) } // WithProcessExecutablePath adds an attribute with the full path to the process // executable to the configured Resource. func WithProcessExecutablePath() Option { return WithDetectors(processExecutablePathDetector{}) } // WithProcessCommandArgs adds an attribute with all the command arguments (including // the command/executable itself) as received by the process the configured Resource. func WithProcessCommandArgs() Option { return WithDetectors(processCommandArgsDetector{}) } // WithProcessOwner adds an attribute with the username of the user that owns the process // to the configured Resource. func WithProcessOwner() Option { return WithDetectors(processOwnerDetector{}) } // WithProcessRuntimeName adds an attribute with the name of the runtime of this // process to the configured Resource. func WithProcessRuntimeName() Option { return WithDetectors(processRuntimeNameDetector{}) } // WithProcessRuntimeVersion adds an attribute with the version of the runtime of // this process to the configured Resource. func WithProcessRuntimeVersion() Option { return WithDetectors(processRuntimeVersionDetector{}) } // WithProcessRuntimeDescription adds an attribute with an additional description // about the runtime of the process to the configured Resource. func WithProcessRuntimeDescription() Option { return WithDetectors(processRuntimeDescriptionDetector{}) } golang-opentelemetry-otel-1.1.0/sdk/resource/doc.go000066400000000000000000000025021414226744000223220ustar00rootroot00000000000000// 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 resource provides detecting and representing resources. // // The fundamental struct is a Resource which holds identifying information // about the entities for which telemetry is exported. // // To automatically construct Resources from an environment a Detector // interface is defined. Implementations of this interface can be passed to // the Detect function to generate a Resource from the merged information. // // To load a user defined Resource from the environment variable // OTEL_RESOURCE_ATTRIBUTES the FromEnv Detector can be used. It will interpret // the value as a list of comma delimited key/value pairs // (e.g. `=,=,...`). package resource // import "go.opentelemetry.io/otel/sdk/resource" golang-opentelemetry-otel-1.1.0/sdk/resource/env.go000066400000000000000000000053711414226744000223540ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "fmt" "os" "strings" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) const ( // resourceAttrKey is the environment variable name OpenTelemetry Resource information will be read from. resourceAttrKey = "OTEL_RESOURCE_ATTRIBUTES" // svcNameKey is the environment variable name that Service Name information will be read from. svcNameKey = "OTEL_SERVICE_NAME" ) var ( // errMissingValue is returned when a resource value is missing. errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource) ) // fromEnv is a Detector that implements the Detector and collects // resources from environment. This Detector is included as a // builtin. type fromEnv struct{} // compile time assertion that FromEnv implements Detector interface var _ Detector = fromEnv{} // Detect collects resources from environment func (fromEnv) Detect(context.Context) (*Resource, error) { attrs := strings.TrimSpace(os.Getenv(resourceAttrKey)) svcName := strings.TrimSpace(os.Getenv(svcNameKey)) if attrs == "" && svcName == "" { return Empty(), nil } var res *Resource if svcName != "" { res = NewSchemaless(semconv.ServiceNameKey.String(svcName)) } r2, err := constructOTResources(attrs) // Ensure that the resource with the service name from OTEL_SERVICE_NAME // takes precedence, if it was defined. res, err2 := Merge(r2, res) if err == nil { err = err2 } else if err2 != nil { err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) } return res, err } func constructOTResources(s string) (*Resource, error) { if s == "" { return Empty(), nil } pairs := strings.Split(s, ",") attrs := []attribute.KeyValue{} var invalid []string for _, p := range pairs { field := strings.SplitN(p, "=", 2) if len(field) != 2 { invalid = append(invalid, p) continue } k, v := strings.TrimSpace(field[0]), strings.TrimSpace(field[1]) attrs = append(attrs, attribute.String(k, v)) } var err error if len(invalid) > 0 { err = fmt.Errorf("%w: %v", errMissingValue, invalid) } return NewSchemaless(attrs...), err } golang-opentelemetry-otel-1.1.0/sdk/resource/env_test.go000066400000000000000000000066341414226744000234160ustar00rootroot00000000000000// 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 resource import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" ottest "go.opentelemetry.io/otel/internal/internaltest" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) func TestDetectOnePair(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ resourceAttrKey: "key=value", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) require.NoError(t, err) assert.Equal(t, NewSchemaless(attribute.String("key", "value")), res) } func TestDetectMultiPairs(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ "x": "1", resourceAttrKey: "key=value, k = v , a= x, a=z", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) require.NoError(t, err) assert.Equal(t, res, NewSchemaless( attribute.String("key", "value"), attribute.String("k", "v"), attribute.String("a", "x"), attribute.String("a", "z"), )) } func TestEmpty(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ resourceAttrKey: " ", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) require.NoError(t, err) assert.Equal(t, Empty(), res) } func TestNoResourceAttributesSet(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ svcNameKey: "bar", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) require.NoError(t, err) assert.Equal(t, res, NewSchemaless( semconv.ServiceNameKey.String("bar"), )) } func TestMissingKeyError(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ resourceAttrKey: "key=value,key", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) assert.Error(t, err) assert.Equal(t, err, fmt.Errorf("%w: %v", errMissingValue, "[key]")) assert.Equal(t, res, NewSchemaless( attribute.String("key", "value"), )) } func TestDetectServiceNameFromEnv(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ resourceAttrKey: "key=value,service.name=foo", svcNameKey: "bar", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() detector := &fromEnv{} res, err := detector.Detect(context.Background()) require.NoError(t, err) assert.Equal(t, res, NewSchemaless( attribute.String("key", "value"), semconv.ServiceNameKey.String("bar"), )) } golang-opentelemetry-otel-1.1.0/sdk/resource/export_common_unix_test.go000066400000000000000000000020511414226744000265470ustar00rootroot00000000000000// 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. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" var ( Uname = uname CharsToString = charsToString GetFirstAvailableFile = getFirstAvailableFile ) var ( SetUnameProvider = setUnameProvider SetDefaultUnameProvider = setDefaultUnameProvider ) golang-opentelemetry-otel-1.1.0/sdk/resource/export_os_release_darwin_test.go000066400000000000000000000013441414226744000277050ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" var ( ParsePlistFile = parsePlistFile BuildOSRelease = buildOSRelease ) golang-opentelemetry-otel-1.1.0/sdk/resource/export_test.go000066400000000000000000000024471414226744000241450ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" var ( SetDefaultOSProviders = setDefaultOSProviders SetOSProviders = setOSProviders SetDefaultRuntimeProviders = setDefaultRuntimeProviders SetRuntimeProviders = setRuntimeProviders SetDefaultUserProviders = setDefaultUserProviders SetUserProviders = setUserProviders SetDefaultOSDescriptionProvider = setDefaultOSDescriptionProvider SetOSDescriptionProvider = setOSDescriptionProvider ) var ( CommandArgs = commandArgs RuntimeName = runtimeName RuntimeOS = runtimeOS RuntimeArch = runtimeArch ) var ( MapRuntimeOSToSemconvOSType = mapRuntimeOSToSemconvOSType ) golang-opentelemetry-otel-1.1.0/sdk/resource/export_unix_test.go000066400000000000000000000017751414226744000252130ustar00rootroot00000000000000// 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. //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" var ( ParseOSReleaseFile = parseOSReleaseFile Skip = skip Parse = parse Unquote = unquote Unescape = unescape BuildOSRelease = buildOSRelease ) golang-opentelemetry-otel-1.1.0/sdk/resource/export_windows_test.go000066400000000000000000000021141414226744000257060ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" var ( PlatformOSDescription = platformOSDescription ReadProductName = readProductName ReadDisplayVersion = readDisplayVersion ReadReleaseID = readReleaseID ReadCurrentMajorVersionNumber = readCurrentMajorVersionNumber ReadCurrentMinorVersionNumber = readCurrentMinorVersionNumber ReadCurrentBuildNumber = readCurrentBuildNumber ReadUBR = readUBR ) golang-opentelemetry-otel-1.1.0/sdk/resource/os.go000066400000000000000000000057271414226744000222120ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "strings" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) type osDescriptionProvider func() (string, error) var defaultOSDescriptionProvider osDescriptionProvider = platformOSDescription var osDescription = defaultOSDescriptionProvider func setDefaultOSDescriptionProvider() { setOSDescriptionProvider(defaultOSDescriptionProvider) } func setOSDescriptionProvider(osDescriptionProvider osDescriptionProvider) { osDescription = osDescriptionProvider } type osTypeDetector struct{} type osDescriptionDetector struct{} // Detect returns a *Resource that describes the operating system type the // service is running on. func (osTypeDetector) Detect(ctx context.Context) (*Resource, error) { osType := runtimeOS() osTypeAttribute := mapRuntimeOSToSemconvOSType(osType) return NewWithAttributes( semconv.SchemaURL, osTypeAttribute, ), nil } // Detect returns a *Resource that describes the operating system the // service is running on. func (osDescriptionDetector) Detect(ctx context.Context) (*Resource, error) { description, err := osDescription() if err != nil { return nil, err } return NewWithAttributes( semconv.SchemaURL, semconv.OSDescriptionKey.String(description), ), nil } // mapRuntimeOSToSemconvOSType translates the OS name as provided by the Go runtime // into an OS type attribute with the corresponding value defined by the semantic // conventions. In case the provided OS name isn't mapped, it's transformed to lowercase // and used as the value for the returned OS type attribute. func mapRuntimeOSToSemconvOSType(osType string) attribute.KeyValue { // the elements in this map are the intersection between // available GOOS values and defined semconv OS types osTypeAttributeMap := map[string]attribute.KeyValue{ "darwin": semconv.OSTypeDarwin, "dragonfly": semconv.OSTypeDragonflyBSD, "freebsd": semconv.OSTypeFreeBSD, "linux": semconv.OSTypeLinux, "netbsd": semconv.OSTypeNetBSD, "openbsd": semconv.OSTypeOpenBSD, "solaris": semconv.OSTypeSolaris, "windows": semconv.OSTypeWindows, } var osTypeAttribute attribute.KeyValue if attr, ok := osTypeAttributeMap[osType]; ok { osTypeAttribute = attr } else { osTypeAttribute = semconv.OSTypeKey.String(strings.ToLower(osType)) } return osTypeAttribute } golang-opentelemetry-otel-1.1.0/sdk/resource/os_release_darwin.go000066400000000000000000000062731414226744000252530ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "encoding/xml" "fmt" "io" "os" ) type plist struct { XMLName xml.Name `xml:"plist"` Dict dict `xml:"dict"` } type dict struct { Key []string `xml:"key"` String []string `xml:"string"` } // osRelease builds a string describing the operating system release based on the // contents of the property list (.plist) system files. If no .plist files are found, // or if the required properties to build the release description string are missing, // an empty string is returned instead. The generated string resembles the output of // the `sw_vers` commandline program, but in a single-line string. For more information // about the `sw_vers` program, see: https://www.unix.com/man-page/osx/1/SW_VERS. func osRelease() string { file, err := getPlistFile() if err != nil { return "" } defer file.Close() values, err := parsePlistFile(file) if err != nil { return "" } return buildOSRelease(values) } // getPlistFile returns a *os.File pointing to one of the well-known .plist files // available on macOS. If no file can be opened, it returns an error. func getPlistFile() (*os.File, error) { return getFirstAvailableFile([]string{ "/System/Library/CoreServices/SystemVersion.plist", "/System/Library/CoreServices/ServerVersion.plist", }) } // parsePlistFile process the file pointed by `file` as a .plist file and returns // a map with the key-values for each pair of correlated and elements // contained in it. func parsePlistFile(file io.Reader) (map[string]string, error) { var v plist err := xml.NewDecoder(file).Decode(&v) if err != nil { return nil, err } if len(v.Dict.Key) != len(v.Dict.String) { return nil, fmt.Errorf("the number of and elements doesn't match") } properties := make(map[string]string, len(v.Dict.Key)) for i, key := range v.Dict.Key { properties[key] = v.Dict.String[i] } return properties, nil } // buildOSRelease builds a string describing the OS release based on the properties // available on the provided map. It tries to find the `ProductName`, `ProductVersion` // and `ProductBuildVersion` properties. If some of these properties are not found, // it returns an empty string. func buildOSRelease(properties map[string]string) string { productName := properties["ProductName"] productVersion := properties["ProductVersion"] productBuildVersion := properties["ProductBuildVersion"] if productName == "" || productVersion == "" || productBuildVersion == "" { return "" } return fmt.Sprintf("%s %s (%s)", productName, productVersion, productBuildVersion) } golang-opentelemetry-otel-1.1.0/sdk/resource/os_release_darwin_test.go000066400000000000000000000113731414226744000263070ustar00rootroot00000000000000// 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 resource_test import ( "bytes" "io" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/resource" ) func TestParsePlistFile(t *testing.T) { standardPlist := bytes.NewBufferString(` ProductBuildVersion 20E232 ProductCopyright 1983-2021 Apple Inc. ProductName macOS ProductUserVisibleVersion 11.3 ProductVersion 11.3 iOSSupportVersion 14.5 `) parsedPlist := map[string]string{ "ProductBuildVersion": "20E232", "ProductCopyright": "1983-2021 Apple Inc.", "ProductName": "macOS", "ProductUserVisibleVersion": "11.3", "ProductVersion": "11.3", "iOSSupportVersion": "14.5", } emptyPlist := bytes.NewBufferString(` `) missingDictPlist := bytes.NewBufferString(` `) unknownElementsPlist := bytes.NewBufferString(` 123 ProductBuildVersion Value 20E232 `) parsedUnknownElementsPlist := map[string]string{ "ProductBuildVersion": "20E232", } tt := []struct { Name string Plist io.Reader Parsed map[string]string }{ {"Standard", standardPlist, parsedPlist}, {"Empty", emptyPlist, map[string]string{}}, {"Missing dict", missingDictPlist, map[string]string{}}, {"Unknown elements", unknownElementsPlist, parsedUnknownElementsPlist}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result, err := resource.ParsePlistFile(tc.Plist) require.Equal(t, tc.Parsed, result) require.NoError(t, err) }) } } func TestParsePlistFileUnevenKeys(t *testing.T) { plist := bytes.NewBufferString(` ProductBuildVersion 20E232 ProductCopyright `) result, err := resource.ParsePlistFile(plist) require.Nil(t, result) require.Error(t, err) } func TestParsePlistFileMalformed(t *testing.T) { plist := bytes.NewBufferString(` Product `) result, err := resource.ParsePlistFile(plist) require.Nil(t, result) require.Error(t, err) } func TestBuildOSRelease(t *testing.T) { tt := []struct { Name string Properties map[string]string OSRelease string }{ {"Empty properties", map[string]string{}, ""}, {"Empty properties (nil)", nil, ""}, {"Missing product name", map[string]string{ "ProductVersion": "11.3", "ProductBuildVersion": "20E232", }, ""}, {"Missing product version", map[string]string{ "ProductName": "macOS", "ProductBuildVersion": "20E232", }, ""}, {"Missing product build version", map[string]string{ "ProductName": "macOS", "ProductVersion": "11.3", }, ""}, {"All properties available", map[string]string{ "ProductName": "macOS", "ProductVersion": "11.3", "ProductBuildVersion": "20E232", }, "macOS 11.3 (20E232)"}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.BuildOSRelease(tc.Properties) require.Equal(t, tc.OSRelease, result) }) } } golang-opentelemetry-otel-1.1.0/sdk/resource/os_release_unix.go000066400000000000000000000112331414226744000247420ustar00rootroot00000000000000// 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. //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "bufio" "fmt" "io" "os" "strings" ) // osRelease builds a string describing the operating system release based on the // properties of the os-release file. If no os-release file is found, or if the // required properties to build the release description string are missing, an empty // string is returned instead. For more information about os-release files, see: // https://www.freedesktop.org/software/systemd/man/os-release.html func osRelease() string { file, err := getOSReleaseFile() if err != nil { return "" } defer file.Close() values := parseOSReleaseFile(file) return buildOSRelease(values) } // getOSReleaseFile returns a *os.File pointing to one of the well-known os-release // files, according to their order of preference. If no file can be opened, it // returns an error. func getOSReleaseFile() (*os.File, error) { return getFirstAvailableFile([]string{"/etc/os-release", "/usr/lib/os-release"}) } // parseOSReleaseFile process the file pointed by `file` as an os-release file and // returns a map with the key-values contained in it. Empty lines or lines starting // with a '#' character are ignored, as well as lines with the missing key=value // separator. Values are unquoted and unescaped. func parseOSReleaseFile(file io.Reader) map[string]string { values := make(map[string]string) scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if skip(line) { continue } key, value, ok := parse(line) if ok { values[key] = value } } return values } // skip returns true if the line is blank or starts with a '#' character, and // therefore should be skipped from processing. func skip(line string) bool { line = strings.TrimSpace(line) return len(line) == 0 || strings.HasPrefix(line, "#") } // parse attempts to split the provided line on the first '=' character, and then // sanitize each side of the split before returning them as a key-value pair. func parse(line string) (string, string, bool) { parts := strings.SplitN(line, "=", 2) if len(parts) != 2 || len(parts[0]) == 0 { return "", "", false } key := strings.TrimSpace(parts[0]) value := unescape(unquote(strings.TrimSpace(parts[1]))) return key, value, true } // unquote checks whether the string `s` is quoted with double or single quotes // and, if so, returns a version of the string without them. Otherwise it returns // the provided string unchanged. func unquote(s string) string { if len(s) < 2 { return s } if (s[0] == '"' || s[0] == '\'') && s[0] == s[len(s)-1] { return s[1 : len(s)-1] } return s } // unescape removes the `\` prefix from some characters that are expected // to have it added in front of them for escaping purposes. func unescape(s string) string { return strings.NewReplacer( `\$`, `$`, `\"`, `"`, `\'`, `'`, `\\`, `\`, "\\`", "`", ).Replace(s) } // buildOSRelease builds a string describing the OS release based on the properties // available on the provided map. It favors a combination of the `NAME` and `VERSION` // properties as first option (falling back to `VERSION_ID` if `VERSION` isn't // found), and using `PRETTY_NAME` alone if some of the previous are not present. If // none of these properties are found, it returns an empty string. // // The rationale behind not using `PRETTY_NAME` as first choice was that, for some // Linux distributions, it doesn't include the same detail that can be found on the // individual `NAME` and `VERSION` properties, and combining `PRETTY_NAME` with // other properties can produce "pretty" redundant strings in some cases. func buildOSRelease(values map[string]string) string { var osRelease string name := values["NAME"] version := values["VERSION"] if version == "" { version = values["VERSION_ID"] } if name != "" && version != "" { osRelease = fmt.Sprintf("%s %s", name, version) } else { osRelease = values["PRETTY_NAME"] } return osRelease } golang-opentelemetry-otel-1.1.0/sdk/resource/os_release_unix_test.go000066400000000000000000000214331414226744000260040ustar00rootroot00000000000000// 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. //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix dragonfly freebsd linux netbsd openbsd solaris zos package resource_test import ( "bytes" "io" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/resource" ) func TestParseOSReleaseFile(t *testing.T) { osReleaseUbuntu := bytes.NewBufferString(`NAME="Ubuntu" VERSION="20.04.2 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 20.04.2 LTS" VERSION_ID="20.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=focal UBUNTU_CODENAME=focal`) parsedUbuntu := map[string]string{ "NAME": "Ubuntu", "VERSION": "20.04.2 LTS (Focal Fossa)", "ID": "ubuntu", "ID_LIKE": "debian", "PRETTY_NAME": "Ubuntu 20.04.2 LTS", "VERSION_ID": "20.04", "HOME_URL": "https://www.ubuntu.com/", "SUPPORT_URL": "https://help.ubuntu.com/", "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", "VERSION_CODENAME": "focal", "UBUNTU_CODENAME": "focal", } osReleaseDebian := bytes.NewBufferString(`PRETTY_NAME="Debian GNU/Linux 10 (buster)" NAME="Debian GNU/Linux" VERSION_ID="10" VERSION="10 (buster)" VERSION_CODENAME=buster ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/"`) parsedDebian := map[string]string{ "PRETTY_NAME": "Debian GNU/Linux 10 (buster)", "NAME": "Debian GNU/Linux", "VERSION_ID": "10", "VERSION": "10 (buster)", "VERSION_CODENAME": "buster", "ID": "debian", "HOME_URL": "https://www.debian.org/", "SUPPORT_URL": "https://www.debian.org/support", "BUG_REPORT_URL": "https://bugs.debian.org/", } osReleaseAlpine := bytes.NewBufferString(`NAME="Alpine Linux" ID=alpine VERSION_ID=3.13.4 PRETTY_NAME="Alpine Linux v3.13" HOME_URL="https://alpinelinux.org/" BUG_REPORT_URL="https://bugs.alpinelinux.org/"`) parsedAlpine := map[string]string{ "NAME": "Alpine Linux", "ID": "alpine", "VERSION_ID": "3.13.4", "PRETTY_NAME": "Alpine Linux v3.13", "HOME_URL": "https://alpinelinux.org/", "BUG_REPORT_URL": "https://bugs.alpinelinux.org/", } osReleaseMock := bytes.NewBufferString(` # This line should be skipped QUOTED1="Quoted value 1" QUOTED2='Quoted value 2' ESCAPED1="\$HOME" ESCAPED2="\"release\"" ESCAPED3="rock\'n\'roll" ESCAPED4="\\var" =line with missing key should be skipped PROP1=name=john PROP2 = Value PROP3='This value will be overwritten by the next one' PROP3='Final value'`) parsedMock := map[string]string{ "QUOTED1": "Quoted value 1", "QUOTED2": "Quoted value 2", "ESCAPED1": "$HOME", "ESCAPED2": `"release"`, "ESCAPED3": "rock'n'roll", "ESCAPED4": `\var`, "PROP1": "name=john", "PROP2": "Value", "PROP3": "Final value", } tt := []struct { Name string OSRelease io.Reader Parsed map[string]string }{ {"Ubuntu", osReleaseUbuntu, parsedUbuntu}, {"Debian", osReleaseDebian, parsedDebian}, {"Alpine", osReleaseAlpine, parsedAlpine}, {"Mock", osReleaseMock, parsedMock}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.ParseOSReleaseFile(tc.OSRelease) require.EqualValues(t, tc.Parsed, result) }) } } func TestSkip(t *testing.T) { tt := []struct { Name string Line string Expected bool }{ {"Empty string", "", true}, {"Only whitespace", " ", true}, {"Hashtag prefix 1", "# Sample text", true}, {"Hashtag prefix 2", " # Sample text", true}, {"Hashtag and whitespace 1", "# ", true}, {"Hashtag and whitespace 2", " #", true}, {"Hashtag and whitespace 3", " # ", true}, {"Nonempty string", "Sample text", false}, {"Nonempty string with whitespace around", " Sample text ", false}, {"Nonempty string with middle hashtag", "Sample #text", false}, {"Nonempty string with ending hashtag", "Sample text #", false}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.Skip(tc.Line) require.EqualValues(t, tc.Expected, result) }) } } func TestParse(t *testing.T) { tt := []struct { Name string Line string ExpectedKey string ExpectedValue string OK bool }{ {"Empty string", "", "", "", false}, {"No separator", "wrong", "", "", false}, {"Empty key", "=john", "", "", false}, {"Empty key value", "=", "", "", false}, {"Empty value", "name=", "name", "", true}, {"Key value 1", "name=john", "name", "john", true}, {"Key value 2", "name=john=dev", "name", "john=dev", true}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { key, value, ok := resource.Parse(tc.Line) require.EqualValues(t, tc.ExpectedKey, key) require.EqualValues(t, tc.ExpectedValue, value) require.EqualValues(t, tc.OK, ok) }) } } func TestUnquote(t *testing.T) { tt := []struct { Name string Text string Expected string }{ {"Empty string", ``, ``}, {"Single double quote", `"`, `"`}, {"Single single quote", `'`, `'`}, {"Empty double quotes", `""`, ``}, {"Empty single quotes", `''`, ``}, {"Empty mixed quotes 1", `"'`, `"'`}, {"Empty mixed quotes 2", `'"`, `'"`}, {"Double quotes", `"Sample text"`, `Sample text`}, {"Single quotes", `'Sample text'`, `Sample text`}, {"Half-open starting double quote", `"Sample text`, `"Sample text`}, {"Half-open ending double quote", `Sample text"`, `Sample text"`}, {"Half-open starting single quote", `'Sample text`, `'Sample text`}, {"Half-open ending single quote", `Sample text'`, `Sample text'`}, {"Double double quotes", `""Sample text""`, `"Sample text"`}, {"Double single quotes", `''Sample text''`, `'Sample text'`}, {"Mismatch quotes 1", `"Sample text'`, `"Sample text'`}, {"Mismatch quotes 2", `'Sample text"`, `'Sample text"`}, {"No quotes", `Sample text`, `Sample text`}, {"Internal double quote", `Sample "text"`, `Sample "text"`}, {"Internal single quote", `Sample 'text'`, `Sample 'text'`}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.Unquote(tc.Text) require.EqualValues(t, tc.Expected, result) }) } } func TestUnescape(t *testing.T) { tt := []struct { Name string Text string Expected string }{ {"Empty string", ``, ``}, {"Escaped dollar sign", `\$var`, `$var`}, {"Escaped double quote", `\"var`, `"var`}, {"Escaped single quote", `\'var`, `'var`}, {"Escaped backslash", `\\var`, `\var`}, {"Escaped backtick", "\\`var", "`var"}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.Unescape(tc.Text) require.EqualValues(t, tc.Expected, result) }) } } func TestBuildOSRelease(t *testing.T) { tt := []struct { Name string Values map[string]string Expected string }{ {"Nil values", nil, ""}, {"Empty values", map[string]string{}, ""}, {"Name and version only", map[string]string{ "NAME": "Ubuntu", "VERSION": "20.04.2 LTS (Focal Fossa)", }, "Ubuntu 20.04.2 LTS (Focal Fossa)"}, {"Name and version preferred", map[string]string{ "NAME": "Ubuntu", "VERSION": "20.04.2 LTS (Focal Fossa)", "VERSION_ID": "20.04", "PRETTY_NAME": "Ubuntu 20.04.2 LTS", }, "Ubuntu 20.04.2 LTS (Focal Fossa)"}, {"Version ID fallback", map[string]string{ "NAME": "Ubuntu", "VERSION_ID": "20.04", }, "Ubuntu 20.04"}, {"Pretty name fallback due to missing name", map[string]string{ "VERSION": "20.04.2 LTS (Focal Fossa)", "PRETTY_NAME": "Ubuntu 20.04.2 LTS", }, "Ubuntu 20.04.2 LTS"}, {"Pretty name fallback due to missing version", map[string]string{ "NAME": "Ubuntu", "PRETTY_NAME": "Ubuntu 20.04.2 LTS", }, "Ubuntu 20.04.2 LTS"}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.BuildOSRelease(tc.Values) require.EqualValues(t, tc.Expected, result) }) } } golang-opentelemetry-otel-1.1.0/sdk/resource/os_test.go000066400000000000000000000036731414226744000232470ustar00rootroot00000000000000// 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 resource_test import ( "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) func mockRuntimeProviders() { resource.SetRuntimeProviders( fakeRuntimeNameProvider, fakeRuntimeVersionProvider, func() string { return "LINUX" }, fakeRuntimeArchProvider, ) resource.SetOSDescriptionProvider( func() (string, error) { return "Test", nil }, ) } func TestMapRuntimeOSToSemconvOSType(t *testing.T) { tt := []struct { Name string Goos string OSType attribute.KeyValue }{ {"Apple Darwin", "darwin", semconv.OSTypeDarwin}, {"DragonFly BSD", "dragonfly", semconv.OSTypeDragonflyBSD}, {"FreeBSD", "freebsd", semconv.OSTypeFreeBSD}, {"Linux", "linux", semconv.OSTypeLinux}, {"NetBSD", "netbsd", semconv.OSTypeNetBSD}, {"OpenBSD", "openbsd", semconv.OSTypeOpenBSD}, {"Oracle Solaris", "solaris", semconv.OSTypeSolaris}, {"Microsoft Windows", "windows", semconv.OSTypeWindows}, {"Unknown", "unknown", semconv.OSTypeKey.String("unknown")}, {"UNKNOWN", "UNKNOWN", semconv.OSTypeKey.String("unknown")}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { osTypeAttribute := resource.MapRuntimeOSToSemconvOSType(tc.Goos) require.EqualValues(t, osTypeAttribute, tc.OSType) }) } } golang-opentelemetry-otel-1.1.0/sdk/resource/os_unix.go000066400000000000000000000055161414226744000232510ustar00rootroot00000000000000// 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. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "bytes" "fmt" "os" "golang.org/x/sys/unix" ) type unameProvider func(buf *unix.Utsname) (err error) var defaultUnameProvider unameProvider = unix.Uname var currentUnameProvider = defaultUnameProvider func setDefaultUnameProvider() { setUnameProvider(defaultUnameProvider) } func setUnameProvider(unameProvider unameProvider) { currentUnameProvider = unameProvider } // platformOSDescription returns a human readable OS version information string. // The final string combines OS release information (where available) and the // result of the `uname` system call. func platformOSDescription() (string, error) { uname, err := uname() if err != nil { return "", err } osRelease := osRelease() if osRelease != "" { return fmt.Sprintf("%s (%s)", osRelease, uname), nil } return uname, nil } // uname issues a uname(2) system call (or equivalent on systems which doesn't // have one) and formats the output in a single string, similar to the output // of the `uname` commandline program. The final string resembles the one // obtained with a call to `uname -snrvm`. func uname() (string, error) { var utsName unix.Utsname err := currentUnameProvider(&utsName) if err != nil { return "", err } return fmt.Sprintf("%s %s %s %s %s", charsToString(utsName.Sysname[:]), charsToString(utsName.Nodename[:]), charsToString(utsName.Release[:]), charsToString(utsName.Version[:]), charsToString(utsName.Machine[:]), ), nil } // charsToString converts a C-like null-terminated char array to a Go string. func charsToString(charArray []byte) string { if i := bytes.IndexByte(charArray, 0); i >= 0 { charArray = charArray[:i] } return string(charArray) } // getFirstAvailableFile returns an *os.File of the first available // file from a list of candidate file paths. func getFirstAvailableFile(candidates []string) (*os.File, error) { for _, c := range candidates { file, err := os.Open(c) if err == nil { return file, nil } } return nil, fmt.Errorf("no candidate file available: %v", candidates) } golang-opentelemetry-otel-1.1.0/sdk/resource/os_unix_test.go000066400000000000000000000075511414226744000243110ustar00rootroot00000000000000// 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. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package resource_test import ( "fmt" "io/ioutil" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/resource" "golang.org/x/sys/unix" ) func fakeUnameProvider(buf *unix.Utsname) error { copy(buf.Sysname[:], "Mock OS") copy(buf.Nodename[:], "DESKTOP-PC") copy(buf.Release[:], "5.0.0") copy(buf.Version[:], "#1 SMP Thu May 6 12:34:56 UTC 2021") copy(buf.Machine[:], "x86_64") return nil } func fakeUnameProviderWithError(buf *unix.Utsname) error { return fmt.Errorf("Error invoking uname(2)") } func TestUname(t *testing.T) { resource.SetUnameProvider(fakeUnameProvider) uname, err := resource.Uname() require.Equal(t, uname, "Mock OS DESKTOP-PC 5.0.0 #1 SMP Thu May 6 12:34:56 UTC 2021 x86_64") require.NoError(t, err) resource.SetDefaultUnameProvider() } func TestUnameError(t *testing.T) { resource.SetUnameProvider(fakeUnameProviderWithError) uname, err := resource.Uname() require.Empty(t, uname) require.Error(t, err) resource.SetDefaultUnameProvider() } func TestCharsToString(t *testing.T) { tt := []struct { Name string Bytes []byte Expected string }{ {"Nil array", nil, ""}, {"Empty array", []byte{}, ""}, {"Empty string (null terminated)", []byte{0x00}, ""}, {"Nonempty string (null terminated)", []byte{0x31, 0x32, 0x33, 0x00}, "123"}, {"Nonempty string (non-null terminated)", []byte{0x31, 0x32, 0x33}, "123"}, {"Nonempty string with values after null", []byte{0x31, 0x32, 0x33, 0x00, 0x34}, "123"}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { result := resource.CharsToString(tc.Bytes) require.EqualValues(t, tc.Expected, result) }) } } func TestGetFirstAvailableFile(t *testing.T) { tempDir := t.TempDir() file1, _ := ioutil.TempFile(tempDir, "candidate_") file2, _ := ioutil.TempFile(tempDir, "candidate_") filename1, filename2 := file1.Name(), file2.Name() tt := []struct { Name string Candidates []string ExpectedFileName string ExpectedErr string }{ {"Gets first, skip second candidate", []string{filename1, filename2}, filename1, ""}, {"Skips first, gets second candidate", []string{"does_not_exists", filename2}, filename2, ""}, {"Skips first, gets second, ignores third candidate", []string{"does_not_exists", filename2, filename1}, filename2, ""}, {"No candidates (empty slice)", []string{}, "", "no candidate file available: []"}, {"No candidates (nil slice)", nil, "", "no candidate file available: []"}, {"Single nonexisting candidate", []string{"does_not_exists"}, "", "no candidate file available: [does_not_exists]"}, {"Multiple nonexisting candidates", []string{"does_not_exists", "this_either"}, "", "no candidate file available: [does_not_exists this_either]"}, } for _, tc := range tt { tc := tc t.Run(tc.Name, func(t *testing.T) { file, err := resource.GetFirstAvailableFile(tc.Candidates) filename := "" if file != nil { filename = file.Name() } errString := "" if err != nil { errString = err.Error() } require.Equal(t, tc.ExpectedFileName, filename) require.Equal(t, tc.ExpectedErr, errString) }) } } golang-opentelemetry-otel-1.1.0/sdk/resource/os_unsupported.go000066400000000000000000000022071414226744000246500ustar00rootroot00000000000000// 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. // +build !aix // +build !darwin // +build !dragonfly // +build !freebsd // +build !linux // +build !netbsd // +build !openbsd // +build !solaris // +build !windows // +build !zos package resource // import "go.opentelemetry.io/otel/sdk/resource" // platformOSDescription is a placeholder implementation for OSes // for which this project currently doesn't support os.description // attribute detection. See build tags declaration early on this file // for a list of unsupported OSes. func platformOSDescription() (string, error) { return "", nil } golang-opentelemetry-otel-1.1.0/sdk/resource/os_windows.go000066400000000000000000000054701414226744000237570ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "fmt" "strconv" "golang.org/x/sys/windows/registry" ) // platformOSDescription returns a human readable OS version information string. // It does so by querying registry values under the // `SOFTWARE\Microsoft\Windows NT\CurrentVersion` key. The final string // resembles the one displayed by the Version Reporter Applet (winver.exe). func platformOSDescription() (string, error) { k, err := registry.OpenKey( registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) if err != nil { return "", err } defer k.Close() var ( productName = readProductName(k) displayVersion = readDisplayVersion(k) releaseID = readReleaseID(k) currentMajorVersionNumber = readCurrentMajorVersionNumber(k) currentMinorVersionNumber = readCurrentMinorVersionNumber(k) currentBuildNumber = readCurrentBuildNumber(k) ubr = readUBR(k) ) if displayVersion != "" { displayVersion += " " } return fmt.Sprintf("%s %s(%s) [Version %s.%s.%s.%s]", productName, displayVersion, releaseID, currentMajorVersionNumber, currentMinorVersionNumber, currentBuildNumber, ubr, ), nil } func getStringValue(name string, k registry.Key) string { value, _, _ := k.GetStringValue(name) return value } func getIntegerValue(name string, k registry.Key) uint64 { value, _, _ := k.GetIntegerValue(name) return value } func readProductName(k registry.Key) string { return getStringValue("ProductName", k) } func readDisplayVersion(k registry.Key) string { return getStringValue("DisplayVersion", k) } func readReleaseID(k registry.Key) string { return getStringValue("ReleaseID", k) } func readCurrentMajorVersionNumber(k registry.Key) string { return strconv.FormatUint(getIntegerValue("CurrentMajorVersionNumber", k), 10) } func readCurrentMinorVersionNumber(k registry.Key) string { return strconv.FormatUint(getIntegerValue("CurrentMinorVersionNumber", k), 10) } func readCurrentBuildNumber(k registry.Key) string { return getStringValue("CurrentBuildNumber", k) } func readUBR(k registry.Key) string { return strconv.FormatUint(getIntegerValue("UBR", k), 10) } golang-opentelemetry-otel-1.1.0/sdk/resource/os_windows_test.go000066400000000000000000000035551414226744000250200ustar00rootroot00000000000000// 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 resource_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/windows/registry" "go.opentelemetry.io/otel/sdk/resource" ) func TestPlatformOSDescription(t *testing.T) { osDescription, err := resource.PlatformOSDescription() require.NoError(t, err) require.Regexp(t, `^(\w+\s)+\(\d+\)\s\[Version\s\d+(\.\d+){3}\]$`, osDescription) } func TestReadRegistryValues(t *testing.T) { k, err := registry.OpenKey( registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) require.NoError(t, err, "should open Windows CurrentVersion registry key") defer k.Close() assert.NotEmpty(t, resource.ReadProductName(k), "should read ProductName") assert.NotEmpty(t, resource.ReadReleaseID(k), "should read ReleaseID") assert.NotEmpty(t, resource.ReadCurrentMajorVersionNumber(k), "should read CurrentMajorVersionNumber") assert.NotEmpty(t, resource.ReadCurrentMinorVersionNumber(k), "should read CurrentMinorVersionNumber") assert.NotEmpty(t, resource.ReadCurrentBuildNumber(k), "should read CurrentBuildNumber") assert.NotEmpty(t, resource.ReadUBR(k), "should read UBR") assert.NotPanics(t, func() { resource.ReadDisplayVersion(k) }, "should not panic when reading DisplayVersion") } golang-opentelemetry-otel-1.1.0/sdk/resource/process.go000066400000000000000000000137311414226744000232410ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "fmt" "os" "os/user" "path/filepath" "runtime" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) type pidProvider func() int type executablePathProvider func() (string, error) type commandArgsProvider func() []string type ownerProvider func() (*user.User, error) type runtimeNameProvider func() string type runtimeVersionProvider func() string type runtimeOSProvider func() string type runtimeArchProvider func() string var ( defaultPidProvider pidProvider = os.Getpid defaultExecutablePathProvider executablePathProvider = os.Executable defaultCommandArgsProvider commandArgsProvider = func() []string { return os.Args } defaultOwnerProvider ownerProvider = user.Current defaultRuntimeNameProvider runtimeNameProvider = func() string { return runtime.Compiler } defaultRuntimeVersionProvider runtimeVersionProvider = runtime.Version defaultRuntimeOSProvider runtimeOSProvider = func() string { return runtime.GOOS } defaultRuntimeArchProvider runtimeArchProvider = func() string { return runtime.GOARCH } ) var ( pid = defaultPidProvider executablePath = defaultExecutablePathProvider commandArgs = defaultCommandArgsProvider owner = defaultOwnerProvider runtimeName = defaultRuntimeNameProvider runtimeVersion = defaultRuntimeVersionProvider runtimeOS = defaultRuntimeOSProvider runtimeArch = defaultRuntimeArchProvider ) func setDefaultOSProviders() { setOSProviders( defaultPidProvider, defaultExecutablePathProvider, defaultCommandArgsProvider, ) } func setOSProviders( pidProvider pidProvider, executablePathProvider executablePathProvider, commandArgsProvider commandArgsProvider, ) { pid = pidProvider executablePath = executablePathProvider commandArgs = commandArgsProvider } func setDefaultRuntimeProviders() { setRuntimeProviders( defaultRuntimeNameProvider, defaultRuntimeVersionProvider, defaultRuntimeOSProvider, defaultRuntimeArchProvider, ) } func setRuntimeProviders( runtimeNameProvider runtimeNameProvider, runtimeVersionProvider runtimeVersionProvider, runtimeOSProvider runtimeOSProvider, runtimeArchProvider runtimeArchProvider, ) { runtimeName = runtimeNameProvider runtimeVersion = runtimeVersionProvider runtimeOS = runtimeOSProvider runtimeArch = runtimeArchProvider } func setDefaultUserProviders() { setUserProviders(defaultOwnerProvider) } func setUserProviders(ownerProvider ownerProvider) { owner = ownerProvider } type processPIDDetector struct{} type processExecutableNameDetector struct{} type processExecutablePathDetector struct{} type processCommandArgsDetector struct{} type processOwnerDetector struct{} type processRuntimeNameDetector struct{} type processRuntimeVersionDetector struct{} type processRuntimeDescriptionDetector struct{} // Detect returns a *Resource that describes the process identifier (PID) of the // executing process. func (processPIDDetector) Detect(ctx context.Context) (*Resource, error) { return NewWithAttributes(semconv.SchemaURL, semconv.ProcessPIDKey.Int(pid())), nil } // Detect returns a *Resource that describes the name of the process executable. func (processExecutableNameDetector) Detect(ctx context.Context) (*Resource, error) { executableName := filepath.Base(commandArgs()[0]) return NewWithAttributes(semconv.SchemaURL, semconv.ProcessExecutableNameKey.String(executableName)), nil } // Detect returns a *Resource that describes the full path of the process executable. func (processExecutablePathDetector) Detect(ctx context.Context) (*Resource, error) { executablePath, err := executablePath() if err != nil { return nil, err } return NewWithAttributes(semconv.SchemaURL, semconv.ProcessExecutablePathKey.String(executablePath)), nil } // Detect returns a *Resource that describes all the command arguments as received // by the process. func (processCommandArgsDetector) Detect(ctx context.Context) (*Resource, error) { return NewWithAttributes(semconv.SchemaURL, semconv.ProcessCommandArgsKey.StringSlice(commandArgs())), nil } // Detect returns a *Resource that describes the username of the user that owns the // process. func (processOwnerDetector) Detect(ctx context.Context) (*Resource, error) { owner, err := owner() if err != nil { return nil, err } return NewWithAttributes(semconv.SchemaURL, semconv.ProcessOwnerKey.String(owner.Username)), nil } // Detect returns a *Resource that describes the name of the compiler used to compile // this process image. func (processRuntimeNameDetector) Detect(ctx context.Context) (*Resource, error) { return NewWithAttributes(semconv.SchemaURL, semconv.ProcessRuntimeNameKey.String(runtimeName())), nil } // Detect returns a *Resource that describes the version of the runtime of this process. func (processRuntimeVersionDetector) Detect(ctx context.Context) (*Resource, error) { return NewWithAttributes(semconv.SchemaURL, semconv.ProcessRuntimeVersionKey.String(runtimeVersion())), nil } // Detect returns a *Resource that describes the runtime of this process. func (processRuntimeDescriptionDetector) Detect(ctx context.Context) (*Resource, error) { runtimeDescription := fmt.Sprintf( "go version %s %s/%s", runtimeVersion(), runtimeOS(), runtimeArch()) return NewWithAttributes( semconv.SchemaURL, semconv.ProcessRuntimeDescriptionKey.String(runtimeDescription), ), nil } golang-opentelemetry-otel-1.1.0/sdk/resource/process_test.go000066400000000000000000000077131414226744000243030ustar00rootroot00000000000000// 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 resource_test import ( "context" "fmt" "os" "os/user" "runtime" "testing" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/resource" ) var ( fakePID = 123 fakeExecutablePath = "/fake/path/mock" fakeCommandArgs = []string{"mock", "-t", "30"} fakeOwner = "gopher" fakeRuntimeName = "gcmock" fakeRuntimeVersion = "go1.2.3" fakeRuntimeOS = "linux" fakeRuntimeArch = "amd64" ) var ( fakeExecutableName = "mock" fakeRuntimeDescription = "go version go1.2.3 linux/amd64" ) var ( fakePidProvider = func() int { return fakePID } fakeExecutablePathProvider = func() (string, error) { return fakeExecutablePath, nil } fakeCommandArgsProvider = func() []string { return fakeCommandArgs } fakeOwnerProvider = func() (*user.User, error) { return &user.User{Username: fakeOwner}, nil } fakeRuntimeNameProvider = func() string { return fakeRuntimeName } fakeRuntimeVersionProvider = func() string { return fakeRuntimeVersion } fakeRuntimeOSProvider = func() string { return fakeRuntimeOS } fakeRuntimeArchProvider = func() string { return fakeRuntimeArch } ) var ( fakeExecutablePathProviderWithError = func() (string, error) { return "", fmt.Errorf("Unable to get process executable") } fakeOwnerProviderWithError = func() (*user.User, error) { return nil, fmt.Errorf("Unable to get process user") } ) func mockProcessAttributesProviders() { resource.SetOSProviders( fakePidProvider, fakeExecutablePathProvider, fakeCommandArgsProvider, ) resource.SetRuntimeProviders( fakeRuntimeNameProvider, fakeRuntimeVersionProvider, fakeRuntimeOSProvider, fakeRuntimeArchProvider, ) resource.SetUserProviders( fakeOwnerProvider, ) } func mockProcessAttributesProvidersWithErrors() { resource.SetOSProviders( fakePidProvider, fakeExecutablePathProviderWithError, fakeCommandArgsProvider, ) resource.SetRuntimeProviders( fakeRuntimeNameProvider, fakeRuntimeVersionProvider, fakeRuntimeOSProvider, fakeRuntimeArchProvider, ) resource.SetUserProviders( fakeOwnerProviderWithError, ) } func restoreAttributesProviders() { resource.SetDefaultOSProviders() resource.SetDefaultRuntimeProviders() resource.SetDefaultUserProviders() resource.SetDefaultOSDescriptionProvider() } func TestWithProcessFuncsErrors(t *testing.T) { mockProcessAttributesProvidersWithErrors() t.Run("WithPID", testWithProcessExecutablePathError) t.Run("WithExecutableName", testWithProcessOwnerError) restoreAttributesProviders() } func TestCommandArgs(t *testing.T) { require.EqualValues(t, os.Args, resource.CommandArgs()) } func TestRuntimeName(t *testing.T) { require.EqualValues(t, runtime.Compiler, resource.RuntimeName()) } func TestRuntimeOS(t *testing.T) { require.EqualValues(t, runtime.GOOS, resource.RuntimeOS()) } func TestRuntimeArch(t *testing.T) { require.EqualValues(t, runtime.GOARCH, resource.RuntimeArch()) } func testWithProcessExecutablePathError(t *testing.T) { ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessExecutablePath(), ) require.Error(t, err) require.EqualValues(t, map[string]string{}, toMap(res)) } func testWithProcessOwnerError(t *testing.T) { ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessOwner(), ) require.Error(t, err) require.EqualValues(t, map[string]string{}, toMap(res)) } golang-opentelemetry-otel-1.1.0/sdk/resource/resource.go000066400000000000000000000165271414226744000234200ustar00rootroot00000000000000// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" import ( "context" "errors" "fmt" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" ) // Resource describes an entity about which identifying information // and metadata is exposed. Resource is an immutable object, // equivalent to a map from key to unique value. // // Resources should be passed and stored as pointers // (`*resource.Resource`). The `nil` value is equivalent to an empty // Resource. type Resource struct { attrs attribute.Set schemaURL string } var ( emptyResource Resource defaultResource = func(r *Resource, err error) *Resource { if err != nil { otel.Handle(err) } return r }( Detect( context.Background(), defaultServiceNameDetector{}, fromEnv{}, telemetrySDK{}, ), ) ) var ( errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflicting Schema URL") ) // New returns a Resource combined from the user-provided detectors. func New(ctx context.Context, opts ...Option) (*Resource, error) { cfg := config{} for _, opt := range opts { opt.apply(&cfg) } resource, err := Detect(ctx, cfg.detectors...) var err2 error resource, err2 = Merge(resource, &Resource{schemaURL: cfg.schemaURL}) if err == nil { err = err2 } else if err2 != nil { err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) } return resource, err } // NewWithAttributes creates a resource from attrs and associates the resource with a // schema URL. If attrs contains duplicate keys, the last value will be used. If attrs // contains any invalid items those items will be dropped. The attrs are assumed to be // in a schema identified by schemaURL. func NewWithAttributes(schemaURL string, attrs ...attribute.KeyValue) *Resource { resource := NewSchemaless(attrs...) resource.schemaURL = schemaURL return resource } // NewSchemaless creates a resource from attrs. If attrs contains duplicate keys, // the last value will be used. If attrs contains any invalid items those items will // be dropped. The resource will not be associated with a schema URL. If the schema // of the attrs is known use NewWithAttributes instead. func NewSchemaless(attrs ...attribute.KeyValue) *Resource { if len(attrs) == 0 { return &emptyResource } // Ensure attributes comply with the specification: // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/common/common.md#attributes s, _ := attribute.NewSetWithFiltered(attrs, func(kv attribute.KeyValue) bool { return kv.Valid() }) // If attrs only contains invalid entries do not allocate a new resource. if s.Len() == 0 { return &emptyResource } return &Resource{attrs: s} //nolint } // String implements the Stringer interface and provides a // human-readable form of the resource. // // Avoid using this representation as the key in a map of resources, // use Equivalent() as the key instead. func (r *Resource) String() string { if r == nil { return "" } return r.attrs.Encoded(attribute.DefaultEncoder()) } // Attributes returns a copy of attributes from the resource in a sorted order. // To avoid allocating a new slice, use an iterator. func (r *Resource) Attributes() []attribute.KeyValue { if r == nil { r = Empty() } return r.attrs.ToSlice() } func (r *Resource) SchemaURL() string { if r == nil { return "" } return r.schemaURL } // Iter returns an iterator of the Resource attributes. // This is ideal to use if you do not want a copy of the attributes. func (r *Resource) Iter() attribute.Iterator { if r == nil { r = Empty() } return r.attrs.Iter() } // Equal returns true when a Resource is equivalent to this Resource. func (r *Resource) Equal(eq *Resource) bool { if r == nil { r = Empty() } if eq == nil { eq = Empty() } return r.Equivalent() == eq.Equivalent() } // Merge creates a new resource by combining resource a and b. // // If there are common keys between resource a and b, then the value // from resource b will overwrite the value from resource a, even // if resource b's value is empty. // // The SchemaURL of the resources will be merged according to the spec rules: // https://github.com/open-telemetry/opentelemetry-specification/blob/bad49c714a62da5493f2d1d9bafd7ebe8c8ce7eb/specification/resource/sdk.md#merge // If the resources have different non-empty schemaURL an empty resource and an error // will be returned. func Merge(a, b *Resource) (*Resource, error) { if a == nil && b == nil { return Empty(), nil } if a == nil { return b, nil } if b == nil { return a, nil } // Merge the schema URL. var schemaURL string if a.schemaURL == "" { schemaURL = b.schemaURL } else if b.schemaURL == "" { schemaURL = a.schemaURL } else if a.schemaURL == b.schemaURL { schemaURL = a.schemaURL } else { return Empty(), errMergeConflictSchemaURL } // Note: 'b' attributes will overwrite 'a' with last-value-wins in attribute.Key() // Meaning this is equivalent to: append(a.Attributes(), b.Attributes()...) mi := attribute.NewMergeIterator(b.Set(), a.Set()) combine := make([]attribute.KeyValue, 0, a.Len()+b.Len()) for mi.Next() { combine = append(combine, mi.Label()) } merged := NewWithAttributes(schemaURL, combine...) return merged, nil } // Empty returns an instance of Resource with no attributes. It is // equivalent to a `nil` Resource. func Empty() *Resource { return &emptyResource } // Default returns an instance of Resource with a default // "service.name" and OpenTelemetrySDK attributes. func Default() *Resource { return defaultResource } // Environment returns an instance of Resource with attributes // extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable. func Environment() *Resource { detector := &fromEnv{} resource, err := detector.Detect(context.Background()) if err != nil { otel.Handle(err) } return resource } // Equivalent returns an object that can be compared for equality // between two resources. This value is suitable for use as a key in // a map. func (r *Resource) Equivalent() attribute.Distinct { return r.Set().Equivalent() } // Set returns the equivalent *attribute.Set of this resource's attributes. func (r *Resource) Set() *attribute.Set { if r == nil { r = Empty() } return &r.attrs } // MarshalJSON encodes the resource attributes as a JSON list of { "Key": // "...", "Value": ... } pairs in order sorted by key. func (r *Resource) MarshalJSON() ([]byte, error) { if r == nil { r = Empty() } return r.attrs.MarshalJSON() } // Len returns the number of unique key-values in this Resource. func (r *Resource) Len() int { if r == nil { return 0 } return r.attrs.Len() } // Encoded returns an encoded representation of the resource. func (r *Resource) Encoded(enc attribute.Encoder) string { if r == nil { return "" } return r.attrs.Encoded(enc) } golang-opentelemetry-otel-1.1.0/sdk/resource/resource_test.go000066400000000000000000000403711414226744000244510ustar00rootroot00000000000000// 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 resource_test import ( "context" "encoding/json" "errors" "fmt" "os" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" ) var ( kv11 = attribute.String("k1", "v11") kv12 = attribute.String("k1", "v12") kv21 = attribute.String("k2", "v21") kv31 = attribute.String("k3", "v31") kv41 = attribute.String("k4", "v41") kv42 = attribute.String("k4", "") ) func TestNewWithAttributes(t *testing.T) { cases := []struct { name string in []attribute.KeyValue want []attribute.KeyValue }{ { name: "Key with common key order1", in: []attribute.KeyValue{kv12, kv11, kv21}, want: []attribute.KeyValue{kv11, kv21}, }, { name: "Key with common key order2", in: []attribute.KeyValue{kv11, kv12, kv21}, want: []attribute.KeyValue{kv12, kv21}, }, { name: "Key with nil", in: nil, want: nil, }, } for _, c := range cases { t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { res := resource.NewSchemaless(c.in...) if diff := cmp.Diff( res.Attributes(), c.want, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("unwanted result: diff %+v,", diff) } }) } } func TestMerge(t *testing.T) { cases := []struct { name string a, b *resource.Resource want []attribute.KeyValue isErr bool schemaURL string }{ { name: "Merge 2 nils", a: nil, b: nil, want: nil, }, { name: "Merge with no overlap, no nil", a: resource.NewSchemaless(kv11, kv31), b: resource.NewSchemaless(kv21, kv41), want: []attribute.KeyValue{kv11, kv21, kv31, kv41}, }, { name: "Merge with no overlap, no nil, not interleaved", a: resource.NewSchemaless(kv11, kv21), b: resource.NewSchemaless(kv31, kv41), want: []attribute.KeyValue{kv11, kv21, kv31, kv41}, }, { name: "Merge with common key order1", a: resource.NewSchemaless(kv11), b: resource.NewSchemaless(kv12, kv21), want: []attribute.KeyValue{kv12, kv21}, }, { name: "Merge with common key order2", a: resource.NewSchemaless(kv12, kv21), b: resource.NewSchemaless(kv11), want: []attribute.KeyValue{kv11, kv21}, }, { name: "Merge with common key order4", a: resource.NewSchemaless(kv11, kv21, kv41), b: resource.NewSchemaless(kv31, kv41), want: []attribute.KeyValue{kv11, kv21, kv31, kv41}, }, { name: "Merge with no keys", a: resource.NewSchemaless(), b: resource.NewSchemaless(), want: nil, }, { name: "Merge with first resource no keys", a: resource.NewSchemaless(), b: resource.NewSchemaless(kv21), want: []attribute.KeyValue{kv21}, }, { name: "Merge with second resource no keys", a: resource.NewSchemaless(kv11), b: resource.NewSchemaless(), want: []attribute.KeyValue{kv11}, }, { name: "Merge with first resource nil", a: nil, b: resource.NewSchemaless(kv21), want: []attribute.KeyValue{kv21}, }, { name: "Merge with second resource nil", a: resource.NewSchemaless(kv11), b: nil, want: []attribute.KeyValue{kv11}, }, { name: "Merge with first resource value empty string", a: resource.NewSchemaless(kv42), b: resource.NewSchemaless(kv41), want: []attribute.KeyValue{kv41}, }, { name: "Merge with second resource value empty string", a: resource.NewSchemaless(kv41), b: resource.NewSchemaless(kv42), want: []attribute.KeyValue{kv42}, }, { name: "Merge with first resource with schema", a: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.4.0", kv41), b: resource.NewSchemaless(kv42), want: []attribute.KeyValue{kv42}, schemaURL: "https://opentelemetry.io/schemas/1.4.0", }, { name: "Merge with second resource with schema", a: resource.NewSchemaless(kv41), b: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.4.0", kv42), want: []attribute.KeyValue{kv42}, schemaURL: "https://opentelemetry.io/schemas/1.4.0", }, { name: "Merge with different schemas", a: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.4.0", kv41), b: resource.NewWithAttributes("https://opentelemetry.io/schemas/1.3.0", kv42), want: nil, isErr: true, }, } for _, c := range cases { t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { res, err := resource.Merge(c.a, c.b) if c.isErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.EqualValues(t, c.schemaURL, res.SchemaURL()) if diff := cmp.Diff( res.Attributes(), c.want, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("unwanted result: diff %+v,", diff) } }) } } func TestEmpty(t *testing.T) { var res *resource.Resource assert.Equal(t, "", res.SchemaURL()) assert.Equal(t, "", res.String()) assert.Equal(t, []attribute.KeyValue(nil), res.Attributes()) it := res.Iter() assert.Equal(t, 0, it.Len()) assert.True(t, res.Equal(res)) } func TestDefault(t *testing.T) { res := resource.Default() require.False(t, res.Equal(resource.Empty())) require.True(t, res.Set().HasValue(semconv.ServiceNameKey)) serviceName, _ := res.Set().Value(semconv.ServiceNameKey) require.True(t, strings.HasPrefix(serviceName.AsString(), "unknown_service:")) require.Greaterf(t, len(serviceName.AsString()), len("unknown_service:"), "default service.name should include executable name") require.Contains(t, res.Attributes(), semconv.TelemetrySDKLanguageGo) require.Contains(t, res.Attributes(), semconv.TelemetrySDKVersionKey.String(otel.Version())) require.Contains(t, res.Attributes(), semconv.TelemetrySDKNameKey.String("opentelemetry")) } func TestString(t *testing.T) { for _, test := range []struct { kvs []attribute.KeyValue want string }{ { kvs: nil, want: "", }, { kvs: []attribute.KeyValue{}, want: "", }, { kvs: []attribute.KeyValue{kv11}, want: "k1=v11", }, { kvs: []attribute.KeyValue{kv11, kv12}, want: "k1=v12", }, { kvs: []attribute.KeyValue{kv11, kv21}, want: "k1=v11,k2=v21", }, { kvs: []attribute.KeyValue{kv21, kv11}, want: "k1=v11,k2=v21", }, { kvs: []attribute.KeyValue{kv11, kv21, kv31}, want: "k1=v11,k2=v21,k3=v31", }, { kvs: []attribute.KeyValue{kv31, kv11, kv21}, want: "k1=v11,k2=v21,k3=v31", }, { kvs: []attribute.KeyValue{attribute.String("A", "a"), attribute.String("B", "b")}, want: "A=a,B=b", }, { kvs: []attribute.KeyValue{attribute.String("A", "a,B=b")}, want: `A=a\,B\=b`, }, { kvs: []attribute.KeyValue{attribute.String("A", `a,B\=b`)}, want: `A=a\,B\\\=b`, }, { kvs: []attribute.KeyValue{attribute.String("A=a,B", `b`)}, want: `A\=a\,B=b`, }, { kvs: []attribute.KeyValue{attribute.String(`A=a\,B`, `b`)}, want: `A\=a\\\,B=b`, }, { kvs: []attribute.KeyValue{attribute.String("", "invalid")}, want: "", }, { kvs: []attribute.KeyValue{attribute.String("", "invalid"), attribute.String("B", "b")}, want: "B=b", }, } { if got := resource.NewSchemaless(test.kvs...).String(); got != test.want { t.Errorf("Resource(%v).String() = %q, want %q", test.kvs, got, test.want) } } } const envVar = "OTEL_RESOURCE_ATTRIBUTES" func TestMarshalJSON(t *testing.T) { r := resource.NewSchemaless(attribute.Int64("A", 1), attribute.String("C", "D")) data, err := json.Marshal(r) require.NoError(t, err) require.Equal(t, `[{"Key":"A","Value":{"Type":"INT64","Value":1}},{"Key":"C","Value":{"Type":"STRING","Value":"D"}}]`, string(data)) } func TestNew(t *testing.T) { tc := []struct { name string envars string detectors []resource.Detector options []resource.Option resourceValues map[string]string schemaURL string isErr bool }{ { name: "No Options returns empty resource", envars: "key=value,other=attr", options: nil, resourceValues: map[string]string{}, }, { name: "Nil Detectors works", envars: "key=value,other=attr", options: []resource.Option{ resource.WithDetectors(), }, resourceValues: map[string]string{}, }, { name: "Only Host", envars: "from=here", options: []resource.Option{ resource.WithHost(), }, resourceValues: map[string]string{ "host.name": hostname(), }, schemaURL: semconv.SchemaURL, }, { name: "Only Env", envars: "key=value,other=attr", options: []resource.Option{ resource.WithFromEnv(), }, resourceValues: map[string]string{ "key": "value", "other": "attr", }, }, { name: "Only TelemetrySDK", envars: "", options: []resource.Option{ resource.WithTelemetrySDK(), }, resourceValues: map[string]string{ "telemetry.sdk.name": "opentelemetry", "telemetry.sdk.language": "go", "telemetry.sdk.version": otel.Version(), }, schemaURL: semconv.SchemaURL, }, { name: "WithAttributes", envars: "key=value,other=attr", options: []resource.Option{ resource.WithAttributes(attribute.String("A", "B")), }, resourceValues: map[string]string{ "A": "B", }, }, { name: "With schema url", envars: "", options: []resource.Option{ resource.WithAttributes(attribute.String("A", "B")), resource.WithSchemaURL("https://opentelemetry.io/schemas/1.0.0"), }, resourceValues: map[string]string{ "A": "B", }, schemaURL: "https://opentelemetry.io/schemas/1.0.0", }, { name: "With conflicting schema urls", envars: "", options: []resource.Option{ resource.WithDetectors( resource.StringDetector("https://opentelemetry.io/schemas/1.0.0", semconv.HostNameKey, os.Hostname), ), resource.WithSchemaURL("https://opentelemetry.io/schemas/1.1.0"), }, resourceValues: map[string]string{}, schemaURL: "", isErr: true, }, { name: "With conflicting detector schema urls", envars: "", options: []resource.Option{ resource.WithDetectors( resource.StringDetector("https://opentelemetry.io/schemas/1.0.0", semconv.HostNameKey, os.Hostname), resource.StringDetector("https://opentelemetry.io/schemas/1.1.0", semconv.HostNameKey, func() (string, error) { return "", errors.New("fail") }), ), resource.WithSchemaURL("https://opentelemetry.io/schemas/1.2.0"), }, resourceValues: map[string]string{}, schemaURL: "", isErr: true, }, } for _, tt := range tc { t.Run(tt.name, func(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ envVar: tt.envars, }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() ctx := context.Background() res, err := resource.New(ctx, tt.options...) if tt.isErr { require.Error(t, err) } else { require.NoError(t, err) } require.EqualValues(t, tt.resourceValues, toMap(res)) // TODO: do we need to ensure that resource is never nil and eliminate the // following if? if res != nil { assert.EqualValues(t, tt.schemaURL, res.SchemaURL()) } }) } } func TestWithOSType(t *testing.T) { mockRuntimeProviders() t.Cleanup(restoreAttributesProviders) ctx := context.Background() res, err := resource.New(ctx, resource.WithOSType(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "os.type": "linux", }, toMap(res)) } func TestWithOSDescription(t *testing.T) { mockRuntimeProviders() t.Cleanup(restoreAttributesProviders) ctx := context.Background() res, err := resource.New(ctx, resource.WithOSDescription(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "os.description": "Test", }, toMap(res)) } func TestWithOS(t *testing.T) { mockRuntimeProviders() t.Cleanup(restoreAttributesProviders) ctx := context.Background() res, err := resource.New(ctx, resource.WithOS(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "os.type": "linux", "os.description": "Test", }, toMap(res)) } func TestWithProcessPID(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessPID(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.pid": fmt.Sprint(fakePID), }, toMap(res)) } func TestWithProcessExecutableName(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessExecutableName(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.executable.name": fakeExecutableName, }, toMap(res)) } func TestWithProcessExecutablePath(t *testing.T) { mockProcessAttributesProviders() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessExecutablePath(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.executable.path": fakeExecutablePath, }, toMap(res)) } func TestWithProcessCommandArgs(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessCommandArgs(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.command_args": fmt.Sprint(fakeCommandArgs), }, toMap(res)) } func TestWithProcessOwner(t *testing.T) { mockProcessAttributesProviders() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessOwner(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.owner": fakeOwner, }, toMap(res)) } func TestWithProcessRuntimeName(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessRuntimeName(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.runtime.name": fakeRuntimeName, }, toMap(res)) } func TestWithProcessRuntimeVersion(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessRuntimeVersion(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.runtime.version": fakeRuntimeVersion, }, toMap(res)) } func TestWithProcessRuntimeDescription(t *testing.T) { mockProcessAttributesProvidersWithErrors() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcessRuntimeDescription(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.runtime.description": fakeRuntimeDescription, }, toMap(res)) } func TestWithProcess(t *testing.T) { mockProcessAttributesProviders() ctx := context.Background() res, err := resource.New(ctx, resource.WithProcess(), ) require.NoError(t, err) require.EqualValues(t, map[string]string{ "process.pid": fmt.Sprint(fakePID), "process.executable.name": fakeExecutableName, "process.executable.path": fakeExecutablePath, "process.command_args": fmt.Sprint(fakeCommandArgs), "process.owner": fakeOwner, "process.runtime.name": fakeRuntimeName, "process.runtime.version": fakeRuntimeVersion, "process.runtime.description": fakeRuntimeDescription, }, toMap(res)) } func toMap(res *resource.Resource) map[string]string { m := map[string]string{} for _, attr := range res.Attributes() { m[string(attr.Key)] = attr.Value.Emit() } return m } func hostname() string { hn, err := os.Hostname() if err != nil { return fmt.Sprintf("hostname(%s)", err) } return hn } golang-opentelemetry-otel-1.1.0/sdk/trace/000077500000000000000000000000001414226744000204765ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/trace/attributesmap.go000066400000000000000000000051701414226744000237140ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "container/list" "go.opentelemetry.io/otel/attribute" ) // attributesMap is a capped map of attributes, holding the most recent attributes. // Eviction is done via a LRU method, the oldest entry is removed to create room for a new entry. // Updates are allowed and they refresh the usage of the key. // // This is based from https://github.com/hashicorp/golang-lru/blob/master/simplelru/lru.go // With a subset of the its operations and specific for holding attribute.KeyValue type attributesMap struct { attributes map[attribute.Key]*list.Element evictList *list.List droppedCount int capacity int } func newAttributesMap(capacity int) *attributesMap { lm := &attributesMap{ attributes: make(map[attribute.Key]*list.Element), evictList: list.New(), capacity: capacity, } return lm } func (am *attributesMap) add(kv attribute.KeyValue) { // Check for existing item if ent, ok := am.attributes[kv.Key]; ok { am.evictList.MoveToFront(ent) ent.Value = &kv return } // Add new item entry := am.evictList.PushFront(&kv) am.attributes[kv.Key] = entry // Verify size not exceeded if am.evictList.Len() > am.capacity { am.removeOldest() am.droppedCount++ } } // toKeyValue copies the attributesMap into a slice of attribute.KeyValue and // returns it. If the map is empty, a nil is returned. // TODO: Is it more efficient to return a pointer to the slice? func (am *attributesMap) toKeyValue() []attribute.KeyValue { len := am.evictList.Len() if len == 0 { return nil } attributes := make([]attribute.KeyValue, 0, len) for ent := am.evictList.Back(); ent != nil; ent = ent.Prev() { if value, ok := ent.Value.(*attribute.KeyValue); ok { attributes = append(attributes, *value) } } return attributes } // removeOldest removes the oldest item from the cache. func (am *attributesMap) removeOldest() { ent := am.evictList.Back() if ent != nil { am.evictList.Remove(ent) kv := ent.Value.(*attribute.KeyValue) delete(am.attributes, kv.Key) } } golang-opentelemetry-otel-1.1.0/sdk/trace/attributesmap_test.go000066400000000000000000000050601414226744000247510ustar00rootroot00000000000000// 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 trace import ( "fmt" "testing" "go.opentelemetry.io/otel/attribute" ) const testKeyFmt = "test-key-%d" func TestAttributesMap(t *testing.T) { wantCapacity := 128 attrMap := newAttributesMap(wantCapacity) for i := 0; i < 256; i++ { attrMap.add(attribute.Int(fmt.Sprintf(testKeyFmt, i), i)) } if attrMap.capacity != wantCapacity { t.Errorf("attrMap.capacity: got '%d'; want '%d'", attrMap.capacity, wantCapacity) } if attrMap.droppedCount != wantCapacity { t.Errorf("attrMap.droppedCount: got '%d'; want '%d'", attrMap.droppedCount, wantCapacity) } for i := 0; i < wantCapacity; i++ { key := attribute.Key(fmt.Sprintf(testKeyFmt, i)) _, ok := attrMap.attributes[key] if ok { t.Errorf("key %q should be dropped", testKeyFmt) } } for i := wantCapacity; i < 256; i++ { key := attribute.Key(fmt.Sprintf(testKeyFmt, i)) _, ok := attrMap.attributes[key] if !ok { t.Errorf("key %q should not be dropped", key) } } } func TestAttributesMapGetOldestRemoveOldest(t *testing.T) { attrMap := newAttributesMap(128) for i := 0; i < 128; i++ { attrMap.add(attribute.Int(fmt.Sprintf(testKeyFmt, i), i)) } attrMap.removeOldest() attrMap.removeOldest() attrMap.removeOldest() for i := 0; i < 3; i++ { key := attribute.Key(fmt.Sprintf(testKeyFmt, i)) _, ok := attrMap.attributes[key] if ok { t.Errorf("key %q should be removed", key) } } } func TestAttributesMapToKeyValue(t *testing.T) { attrMap := newAttributesMap(128) for i := 0; i < 128; i++ { attrMap.add(attribute.Int(fmt.Sprintf(testKeyFmt, i), i)) } kv := attrMap.toKeyValue() gotAttrLen := len(kv) wantAttrLen := 128 if gotAttrLen != wantAttrLen { t.Errorf("len(attrMap.attributes): got '%d'; want '%d'", gotAttrLen, wantAttrLen) } } func BenchmarkAttributesMapToKeyValue(b *testing.B) { attrMap := newAttributesMap(128) for i := 0; i < 128; i++ { attrMap.add(attribute.Int(fmt.Sprintf(testKeyFmt, i), i)) } for n := 0; n < b.N; n++ { attrMap.toKeyValue() } } golang-opentelemetry-otel-1.1.0/sdk/trace/batch_span_processor.go000066400000000000000000000210421414226744000252250ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "runtime" "sync" "sync/atomic" "time" "go.opentelemetry.io/otel" ) // Defaults for BatchSpanProcessorOptions. const ( DefaultMaxQueueSize = 2048 DefaultBatchTimeout = 5000 * time.Millisecond DefaultExportTimeout = 30000 * time.Millisecond DefaultMaxExportBatchSize = 512 ) type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions) type BatchSpanProcessorOptions struct { // MaxQueueSize is the maximum queue size to buffer spans for delayed processing. If the // queue gets full it drops the spans. Use BlockOnQueueFull to change this behavior. // The default value of MaxQueueSize is 2048. MaxQueueSize int // BatchTimeout is the maximum duration for constructing a batch. Processor // forcefully sends available spans when timeout is reached. // The default value of BatchTimeout is 5000 msec. BatchTimeout time.Duration // ExportTimeout specifies the maximum duration for exporting spans. If the timeout // is reached, the export will be cancelled. // The default value of ExportTimeout is 30000 msec. ExportTimeout time.Duration // MaxExportBatchSize is the maximum number of spans to process in a single batch. // If there are more than one batch worth of spans then it processes multiple batches // of spans one batch after the other without any delay. // The default value of MaxExportBatchSize is 512. MaxExportBatchSize int // BlockOnQueueFull blocks onEnd() and onStart() method if the queue is full // AND if BlockOnQueueFull is set to true. // Blocking option should be used carefully as it can severely affect the performance of an // application. BlockOnQueueFull bool } // batchSpanProcessor is a SpanProcessor that batches asynchronously-received // spans and sends them to a trace.Exporter when complete. type batchSpanProcessor struct { e SpanExporter o BatchSpanProcessorOptions queue chan ReadOnlySpan dropped uint32 batch []ReadOnlySpan batchMutex sync.Mutex timer *time.Timer stopWait sync.WaitGroup stopOnce sync.Once stopCh chan struct{} } var _ SpanProcessor = (*batchSpanProcessor)(nil) // NewBatchSpanProcessor creates a new SpanProcessor that will send completed // span batches to the exporter with the supplied options. // // If the exporter is nil, the span processor will preform no action. func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorOption) SpanProcessor { o := BatchSpanProcessorOptions{ BatchTimeout: DefaultBatchTimeout, ExportTimeout: DefaultExportTimeout, MaxQueueSize: DefaultMaxQueueSize, MaxExportBatchSize: DefaultMaxExportBatchSize, } for _, opt := range options { opt(&o) } bsp := &batchSpanProcessor{ e: exporter, o: o, batch: make([]ReadOnlySpan, 0, o.MaxExportBatchSize), timer: time.NewTimer(o.BatchTimeout), queue: make(chan ReadOnlySpan, o.MaxQueueSize), stopCh: make(chan struct{}), } bsp.stopWait.Add(1) go func() { defer bsp.stopWait.Done() bsp.processQueue() bsp.drainQueue() }() return bsp } // OnStart method does nothing. func (bsp *batchSpanProcessor) OnStart(parent context.Context, s ReadWriteSpan) {} // OnEnd method enqueues a ReadOnlySpan for later processing. func (bsp *batchSpanProcessor) OnEnd(s ReadOnlySpan) { // Do not enqueue spans if we are just going to drop them. if bsp.e == nil { return } bsp.enqueue(s) } // Shutdown flushes the queue and waits until all spans are processed. // It only executes once. Subsequent call does nothing. func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error { var err error bsp.stopOnce.Do(func() { wait := make(chan struct{}) go func() { close(bsp.stopCh) bsp.stopWait.Wait() if bsp.e != nil { if err := bsp.e.Shutdown(ctx); err != nil { otel.Handle(err) } } close(wait) }() // Wait until the wait group is done or the context is cancelled select { case <-wait: case <-ctx.Done(): err = ctx.Err() } }) return err } // ForceFlush exports all ended spans that have not yet been exported. func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error { var err error if bsp.e != nil { wait := make(chan error) go func() { wait <- bsp.exportSpans(ctx) close(wait) }() // Wait until the export is finished or the context is cancelled/timed out select { case err = <-wait: case <-ctx.Done(): err = ctx.Err() } } return err } func WithMaxQueueSize(size int) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.MaxQueueSize = size } } func WithMaxExportBatchSize(size int) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.MaxExportBatchSize = size } } func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.BatchTimeout = delay } } func WithExportTimeout(timeout time.Duration) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.ExportTimeout = timeout } } func WithBlocking() BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.BlockOnQueueFull = true } } // exportSpans is a subroutine of processing and draining the queue. func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { bsp.timer.Reset(bsp.o.BatchTimeout) bsp.batchMutex.Lock() defer bsp.batchMutex.Unlock() if bsp.o.ExportTimeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, bsp.o.ExportTimeout) defer cancel() } if l := len(bsp.batch); l > 0 { err := bsp.e.ExportSpans(ctx, bsp.batch) // A new batch is always created after exporting, even if the batch failed to be exported. // // It is up to the exporter to implement any type of retry logic if a batch is failing // to be exported, since it is specific to the protocol and backend being sent to. bsp.batch = bsp.batch[:0] if err != nil { return err } } return nil } // processQueue removes spans from the `queue` channel until processor // is shut down. It calls the exporter in batches of up to MaxExportBatchSize // waiting up to BatchTimeout to form a batch. func (bsp *batchSpanProcessor) processQueue() { defer bsp.timer.Stop() ctx, cancel := context.WithCancel(context.Background()) defer cancel() for { select { case <-bsp.stopCh: return case <-bsp.timer.C: if err := bsp.exportSpans(ctx); err != nil { otel.Handle(err) } case sd := <-bsp.queue: bsp.batchMutex.Lock() bsp.batch = append(bsp.batch, sd) shouldExport := len(bsp.batch) >= bsp.o.MaxExportBatchSize bsp.batchMutex.Unlock() if shouldExport { if !bsp.timer.Stop() { <-bsp.timer.C } if err := bsp.exportSpans(ctx); err != nil { otel.Handle(err) } } } } } // drainQueue awaits the any caller that had added to bsp.stopWait // to finish the enqueue, then exports the final batch. func (bsp *batchSpanProcessor) drainQueue() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for { select { case sd := <-bsp.queue: if sd == nil { if err := bsp.exportSpans(ctx); err != nil { otel.Handle(err) } return } bsp.batchMutex.Lock() bsp.batch = append(bsp.batch, sd) shouldExport := len(bsp.batch) == bsp.o.MaxExportBatchSize bsp.batchMutex.Unlock() if shouldExport { if err := bsp.exportSpans(ctx); err != nil { otel.Handle(err) } } default: close(bsp.queue) } } } func (bsp *batchSpanProcessor) enqueue(sd ReadOnlySpan) { if !sd.SpanContext().IsSampled() { return } // This ensures the bsp.queue<- below does not panic as the // processor shuts down. defer func() { x := recover() switch err := x.(type) { case nil: return case runtime.Error: if err.Error() == "send on closed channel" { return } } panic(x) }() select { case <-bsp.stopCh: return default: } if bsp.o.BlockOnQueueFull { bsp.queue <- sd return } select { case bsp.queue <- sd: default: atomic.AddUint32(&bsp.dropped, 1) } } golang-opentelemetry-otel-1.1.0/sdk/trace/batch_span_processor_test.go000066400000000000000000000305111414226744000262650ustar00rootroot00000000000000// 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 trace_test import ( "context" "encoding/binary" "errors" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) type testBatchExporter struct { mu sync.Mutex spans []sdktrace.ReadOnlySpan sizes []int batchCount int shutdownCount int errors []error droppedCount int idx int err error } func (t *testBatchExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { t.mu.Lock() defer t.mu.Unlock() if t.idx < len(t.errors) { t.droppedCount += len(spans) err := t.errors[t.idx] t.idx++ return err } select { case <-ctx.Done(): t.err = ctx.Err() return ctx.Err() default: } t.spans = append(t.spans, spans...) t.sizes = append(t.sizes, len(spans)) t.batchCount++ return nil } func (t *testBatchExporter) Shutdown(context.Context) error { t.shutdownCount++ return nil } func (t *testBatchExporter) len() int { t.mu.Lock() defer t.mu.Unlock() return len(t.spans) } func (t *testBatchExporter) getBatchCount() int { t.mu.Lock() defer t.mu.Unlock() return t.batchCount } var _ sdktrace.SpanExporter = (*testBatchExporter)(nil) func TestNewBatchSpanProcessorWithNilExporter(t *testing.T) { tp := basicTracerProvider(t) bsp := sdktrace.NewBatchSpanProcessor(nil) tp.RegisterSpanProcessor(bsp) tr := tp.Tracer("NilExporter") _, span := tr.Start(context.Background(), "foo") span.End() // These should not panic. bsp.OnStart(context.Background(), span.(sdktrace.ReadWriteSpan)) bsp.OnEnd(span.(sdktrace.ReadOnlySpan)) if err := bsp.ForceFlush(context.Background()); err != nil { t.Errorf("failed to ForceFlush the BatchSpanProcessor: %v", err) } if err := bsp.Shutdown(context.Background()); err != nil { t.Errorf("failed to Shutdown the BatchSpanProcessor: %v", err) } } type testOption struct { name string o []sdktrace.BatchSpanProcessorOption wantNumSpans int wantBatchCount int genNumSpans int parallel bool } func TestNewBatchSpanProcessorWithOptions(t *testing.T) { schDelay := 200 * time.Millisecond options := []testOption{ { name: "default BatchSpanProcessorOptions", wantNumSpans: 2053, wantBatchCount: 4, genNumSpans: 2053, }, { name: "non-default BatchTimeout", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), }, wantNumSpans: 2053, wantBatchCount: 4, genNumSpans: 2053, }, { name: "non-default MaxQueueSize and BatchTimeout", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), }, wantNumSpans: 205, wantBatchCount: 1, genNumSpans: 205, }, { name: "non-default MaxQueueSize, BatchTimeout and MaxExportBatchSize", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(205), sdktrace.WithMaxExportBatchSize(20), }, wantNumSpans: 210, wantBatchCount: 11, genNumSpans: 210, }, { name: "blocking option", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), sdktrace.WithMaxExportBatchSize(20), }, wantNumSpans: 205, wantBatchCount: 11, genNumSpans: 205, }, { name: "parallel span generation", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxQueueSize(200), }, wantNumSpans: 205, wantBatchCount: 1, genNumSpans: 205, parallel: true, }, { name: "parallel span blocking", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithBatchTimeout(schDelay), sdktrace.WithMaxExportBatchSize(200), }, wantNumSpans: 2000, wantBatchCount: 10, genNumSpans: 2000, parallel: true, }, } for _, option := range options { t.Run(option.name, func(t *testing.T) { te := testBatchExporter{} tp := basicTracerProvider(t) ssp := createAndRegisterBatchSP(option, &te) if ssp == nil { t.Fatalf("%s: Error creating new instance of BatchSpanProcessor\n", option.name) } tp.RegisterSpanProcessor(ssp) tr := tp.Tracer("BatchSpanProcessorWithOptions") generateSpan(t, option.parallel, tr, option) tp.UnregisterSpanProcessor(ssp) gotNumOfSpans := te.len() if option.wantNumSpans > 0 && option.wantNumSpans != gotNumOfSpans { t.Errorf("number of exported span: got %+v, want %+v\n", gotNumOfSpans, option.wantNumSpans) } gotBatchCount := te.getBatchCount() if option.wantBatchCount > 0 && gotBatchCount < option.wantBatchCount { t.Errorf("number batches: got %+v, want >= %+v\n", gotBatchCount, option.wantBatchCount) t.Errorf("Batches %v\n", te.sizes) } }) } } type stuckExporter struct { testBatchExporter } // ExportSpans waits for ctx to expire and returns that error. func (e *stuckExporter) ExportSpans(ctx context.Context, _ []sdktrace.ReadOnlySpan) error { <-ctx.Done() e.err = ctx.Err() return ctx.Err() } func TestBatchSpanProcessorExportTimeout(t *testing.T) { exp := new(stuckExporter) bsp := sdktrace.NewBatchSpanProcessor( exp, // Set a non-zero export timeout so a deadline is set. sdktrace.WithExportTimeout(1*time.Microsecond), sdktrace.WithBlocking(), ) tp := basicTracerProvider(t) tp.RegisterSpanProcessor(bsp) tr := tp.Tracer("BatchSpanProcessorExportTimeout") generateSpan(t, false, tr, testOption{genNumSpans: 1}) tp.UnregisterSpanProcessor(bsp) if exp.err != context.DeadlineExceeded { t.Errorf("context deadline error not returned: got %+v", exp.err) } } func createAndRegisterBatchSP(option testOption, te *testBatchExporter) sdktrace.SpanProcessor { // Always use blocking queue to avoid flaky tests. options := append(option.o, sdktrace.WithBlocking()) return sdktrace.NewBatchSpanProcessor(te, options...) } func generateSpan(t *testing.T, parallel bool, tr trace.Tracer, option testOption) { sc := getSpanContext() wg := &sync.WaitGroup{} for i := 0; i < option.genNumSpans; i++ { tid := sc.TraceID() binary.BigEndian.PutUint64(tid[0:8], uint64(i+1)) newSc := sc.WithTraceID(tid) wg.Add(1) f := func(sc trace.SpanContext) { ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) _, span := tr.Start(ctx, option.name) span.End() wg.Done() } if parallel { go f(newSc) } else { f(newSc) } } wg.Wait() } func getSpanContext() trace.SpanContext { tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ := trace.SpanIDFromHex("0102040810203040") return trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, }) } func TestBatchSpanProcessorShutdown(t *testing.T) { var bp testBatchExporter bsp := sdktrace.NewBatchSpanProcessor(&bp) err := bsp.Shutdown(context.Background()) if err != nil { t.Error("Error shutting the BatchSpanProcessor down\n") } assert.Equal(t, 1, bp.shutdownCount, "shutdown from span exporter not called") // Multiple call to Shutdown() should not panic. err = bsp.Shutdown(context.Background()) if err != nil { t.Error("Error shutting the BatchSpanProcessor down\n") } assert.Equal(t, 1, bp.shutdownCount) } func TestBatchSpanProcessorPostShutdown(t *testing.T) { tp := basicTracerProvider(t) be := testBatchExporter{} bsp := sdktrace.NewBatchSpanProcessor(&be) tp.RegisterSpanProcessor(bsp) tr := tp.Tracer("Normal") generateSpan(t, true, tr, testOption{ o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithMaxExportBatchSize(50), }, genNumSpans: 60, }) require.NoError(t, bsp.Shutdown(context.Background()), "shutting down BatchSpanProcessor") lenJustAfterShutdown := be.len() _, span := tr.Start(context.Background(), "foo") span.End() assert.NoError(t, bsp.ForceFlush(context.Background()), "force flushing BatchSpanProcessor") assert.Equal(t, lenJustAfterShutdown, be.len(), "OnEnd and ForceFlush should have no effect after Shutdown") } func TestBatchSpanProcessorForceFlushSucceeds(t *testing.T) { te := testBatchExporter{} tp := basicTracerProvider(t) option := testOption{ name: "default BatchSpanProcessorOptions", o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithMaxQueueSize(0), sdktrace.WithMaxExportBatchSize(3000), }, wantNumSpans: 2053, wantBatchCount: 1, genNumSpans: 2053, } ssp := createAndRegisterBatchSP(option, &te) if ssp == nil { t.Fatalf("%s: Error creating new instance of BatchSpanProcessor\n", option.name) } tp.RegisterSpanProcessor(ssp) tr := tp.Tracer("BatchSpanProcessorWithOption") generateSpan(t, option.parallel, tr, option) // Force flush any held span batches err := ssp.ForceFlush(context.Background()) assertMaxSpanDiff(t, te.len(), option.wantNumSpans, 10) gotBatchCount := te.getBatchCount() if gotBatchCount < option.wantBatchCount { t.Errorf("number batches: got %+v, want >= %+v\n", gotBatchCount, option.wantBatchCount) t.Errorf("Batches %v\n", te.sizes) } assert.NoError(t, err) } func TestBatchSpanProcessorDropBatchIfFailed(t *testing.T) { te := testBatchExporter{ errors: []error{errors.New("fail to export")}, } tp := basicTracerProvider(t) option := testOption{ o: []sdktrace.BatchSpanProcessorOption{ sdktrace.WithMaxQueueSize(0), sdktrace.WithMaxExportBatchSize(2000), }, wantNumSpans: 1000, wantBatchCount: 1, genNumSpans: 1000, } ssp := createAndRegisterBatchSP(option, &te) if ssp == nil { t.Fatalf("%s: Error creating new instance of BatchSpanProcessor\n", option.name) } tp.RegisterSpanProcessor(ssp) tr := tp.Tracer("BatchSpanProcessorWithOption") generateSpan(t, option.parallel, tr, option) // Force flush any held span batches err := ssp.ForceFlush(context.Background()) assert.Error(t, err) assert.EqualError(t, err, "fail to export") // First flush will fail, nothing should be exported. assertMaxSpanDiff(t, te.droppedCount, option.wantNumSpans, 10) assert.Equal(t, 0, te.len()) assert.Equal(t, 0, te.getBatchCount()) // Generate a new batch, this will succeed generateSpan(t, option.parallel, tr, option) // Force flush any held span batches err = ssp.ForceFlush(context.Background()) assert.NoError(t, err) assertMaxSpanDiff(t, te.len(), option.wantNumSpans, 10) gotBatchCount := te.getBatchCount() if gotBatchCount < option.wantBatchCount { t.Errorf("number batches: got %+v, want >= %+v\n", gotBatchCount, option.wantBatchCount) t.Errorf("Batches %v\n", te.sizes) } } func assertMaxSpanDiff(t *testing.T, want, got, maxDif int) { spanDifference := want - got if spanDifference < 0 { spanDifference = spanDifference * -1 } if spanDifference > maxDif { t.Errorf("number of exported span not equal to or within %d less than: got %+v, want %+v\n", maxDif, got, want) } } type indefiniteExporter struct{} func (indefiniteExporter) Shutdown(context.Context) error { return nil } func (indefiniteExporter) ExportSpans(ctx context.Context, _ []sdktrace.ReadOnlySpan) error { <-ctx.Done() return ctx.Err() } func TestBatchSpanProcessorForceFlushTimeout(t *testing.T) { // Add timeout to context to test deadline ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) defer cancel() <-ctx.Done() bsp := sdktrace.NewBatchSpanProcessor(indefiniteExporter{}) if got, want := bsp.ForceFlush(ctx), context.DeadlineExceeded; !errors.Is(got, want) { t.Errorf("expected %q error, got %v", want, got) } } func TestBatchSpanProcessorForceFlushCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) // Cancel the context cancel() bsp := sdktrace.NewBatchSpanProcessor(indefiniteExporter{}) if got, want := bsp.ForceFlush(ctx), context.Canceled; !errors.Is(got, want) { t.Errorf("expected %q error, got %v", want, got) } } golang-opentelemetry-otel-1.1.0/sdk/trace/benchmark_test.go000066400000000000000000000103401414226744000240140ustar00rootroot00000000000000// 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 trace_test import ( "context" "testing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) func BenchmarkStartEndSpan(b *testing.B) { traceBenchmark(b, "Benchmark StartEndSpan", func(b *testing.B, t trace.Tracer) { ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.End() } }) } func BenchmarkSpanWithAttributes_4(b *testing.B) { traceBenchmark(b, "Benchmark Start With 4 Attributes", func(b *testing.B, t trace.Tracer) { ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.SetAttributes( attribute.Bool("key1", false), attribute.String("key2", "hello"), attribute.Float64("key4", 123.456), ) span.End() } }) } func BenchmarkSpanWithAttributes_8(b *testing.B) { traceBenchmark(b, "Benchmark Start With 8 Attributes", func(b *testing.B, t trace.Tracer) { ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.SetAttributes( attribute.Bool("key1", false), attribute.String("key2", "hello"), attribute.Float64("key4", 123.456), attribute.Bool("key21", false), attribute.String("key22", "hello"), attribute.Float64("key24", 123.456), ) span.End() } }) } func BenchmarkSpanWithAttributes_all(b *testing.B) { traceBenchmark(b, "Benchmark Start With all Attribute types", func(b *testing.B, t trace.Tracer) { ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.SetAttributes( attribute.Bool("key1", false), attribute.String("key2", "hello"), attribute.Int64("key3", 123), attribute.Float64("key7", 123.456), attribute.Int("key9", 123), ) span.End() } }) } func BenchmarkSpanWithAttributes_all_2x(b *testing.B) { traceBenchmark(b, "Benchmark Start With all Attributes types twice", func(b *testing.B, t trace.Tracer) { ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, span := t.Start(ctx, "/foo") span.SetAttributes( attribute.Bool("key1", false), attribute.String("key2", "hello"), attribute.Int64("key3", 123), attribute.Float64("key7", 123.456), attribute.Int("key10", 123), attribute.Bool("key21", false), attribute.String("key22", "hello"), attribute.Int64("key23", 123), attribute.Float64("key27", 123.456), attribute.Int("key210", 123), ) span.End() } }) } func BenchmarkTraceID_DotString(b *testing.B) { t, _ := trace.TraceIDFromHex("0000000000000001000000000000002a") sc := trace.NewSpanContext(trace.SpanContextConfig{TraceID: t}) want := "0000000000000001000000000000002a" for i := 0; i < b.N; i++ { if got := sc.TraceID().String(); got != want { b.Fatalf("got = %q want = %q", got, want) } } } func BenchmarkSpanID_DotString(b *testing.B) { sc := trace.NewSpanContext(trace.SpanContextConfig{SpanID: trace.SpanID{1}}) want := "0100000000000000" for i := 0; i < b.N; i++ { if got := sc.SpanID().String(); got != want { b.Fatalf("got = %q want = %q", got, want) } } } func traceBenchmark(b *testing.B, name string, fn func(*testing.B, trace.Tracer)) { b.Run("AlwaysSample", func(b *testing.B) { b.ReportAllocs() fn(b, tracer(b, name, sdktrace.AlwaysSample())) }) b.Run("NeverSample", func(b *testing.B) { b.ReportAllocs() fn(b, tracer(b, name, sdktrace.NeverSample())) }) } func tracer(b *testing.B, name string, sampler sdktrace.Sampler) trace.Tracer { tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sampler)) return tp.Tracer(name) } golang-opentelemetry-otel-1.1.0/sdk/trace/config.go000066400000000000000000000045051414226744000222760ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" // SpanLimits represents the limits of a span. type SpanLimits struct { // AttributeCountLimit is the maximum allowed span attribute count. AttributeCountLimit int // EventCountLimit is the maximum allowed span event count. EventCountLimit int // LinkCountLimit is the maximum allowed span link count. LinkCountLimit int // AttributePerEventCountLimit is the maximum allowed attribute per span event count. AttributePerEventCountLimit int // AttributePerLinkCountLimit is the maximum allowed attribute per span link count. AttributePerLinkCountLimit int } func (sl *SpanLimits) ensureDefault() { if sl.EventCountLimit <= 0 { sl.EventCountLimit = DefaultEventCountLimit } if sl.AttributeCountLimit <= 0 { sl.AttributeCountLimit = DefaultAttributeCountLimit } if sl.LinkCountLimit <= 0 { sl.LinkCountLimit = DefaultLinkCountLimit } if sl.AttributePerEventCountLimit <= 0 { sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit } if sl.AttributePerLinkCountLimit <= 0 { sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit } } const ( // DefaultAttributeCountLimit is the default maximum allowed span attribute count. DefaultAttributeCountLimit = 128 // DefaultEventCountLimit is the default maximum allowed span event count. DefaultEventCountLimit = 128 // DefaultLinkCountLimit is the default maximum allowed span link count. DefaultLinkCountLimit = 128 // DefaultAttributePerEventCountLimit is the default maximum allowed attribute per span event count. DefaultAttributePerEventCountLimit = 128 // DefaultAttributePerLinkCountLimit is the default maximum allowed attribute per span link count. DefaultAttributePerLinkCountLimit = 128 ) golang-opentelemetry-otel-1.1.0/sdk/trace/doc.go000066400000000000000000000015051414226744000215730ustar00rootroot00000000000000// 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 trace contains support for OpenTelemetry distributed tracing. The following assumes a basic familiarity with OpenTelemetry concepts. See https://opentelemetry.io. */ package trace // import "go.opentelemetry.io/otel/sdk/trace" golang-opentelemetry-otel-1.1.0/sdk/trace/event.go000066400000000000000000000021661414226744000221530ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "time" "go.opentelemetry.io/otel/attribute" ) // Event is a thing that happened during a Span's lifetime. type Event struct { // Name is the name of this event Name string // Attributes describe the aspects of the event. Attributes []attribute.KeyValue // DroppedAttributeCount is the number of attributes that were not // recorded due to configured limits being reached. DroppedAttributeCount int // Time at which this event was recorded. Time time.Time } golang-opentelemetry-otel-1.1.0/sdk/trace/evictedqueue.go000066400000000000000000000020621414226744000235150ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" type evictedQueue struct { queue []interface{} capacity int droppedCount int } func newEvictedQueue(capacity int) *evictedQueue { eq := &evictedQueue{ capacity: capacity, queue: make([]interface{}, 0), } return eq } func (eq *evictedQueue) add(value interface{}) { if len(eq.queue) == eq.capacity { eq.queue = eq.queue[1:] eq.droppedCount++ } eq.queue = append(eq.queue, value) } golang-opentelemetry-otel-1.1.0/sdk/trace/evictedqueue_test.go000066400000000000000000000033641414226744000245620ustar00rootroot00000000000000// 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 trace import ( "reflect" "testing" ) func init() { } func TestAdd(t *testing.T) { q := newEvictedQueue(3) q.add("value1") q.add("value2") if wantLen, gotLen := 2, len(q.queue); wantLen != gotLen { t.Errorf("got queue length %d want %d", gotLen, wantLen) } } func (eq *evictedQueue) queueToArray() []string { arr := make([]string, 0) for _, value := range eq.queue { arr = append(arr, value.(string)) } return arr } func TestDropCount(t *testing.T) { q := newEvictedQueue(3) q.add("value1") q.add("value2") q.add("value3") q.add("value1") q.add("value4") if wantLen, gotLen := 3, len(q.queue); wantLen != gotLen { t.Errorf("got queue length %d want %d", gotLen, wantLen) } if wantDropCount, gotDropCount := 2, q.droppedCount; wantDropCount != gotDropCount { t.Errorf("got drop count %d want %d", gotDropCount, wantDropCount) } wantArr := []string{"value3", "value1", "value4"} gotArr := q.queueToArray() if wantLen, gotLen := len(wantArr), len(gotArr); gotLen != wantLen { t.Errorf("got array len %d want %d", gotLen, wantLen) } if !reflect.DeepEqual(gotArr, wantArr) { t.Errorf("got array = %#v; want %#v", gotArr, wantArr) } } golang-opentelemetry-otel-1.1.0/sdk/trace/id_generator.go000066400000000000000000000046141414226744000234740ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" crand "crypto/rand" "encoding/binary" "math/rand" "sync" "go.opentelemetry.io/otel/trace" ) // IDGenerator allows custom generators for TraceID and SpanID. type IDGenerator interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // NewIDs returns a new trace and span ID. NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // NewSpanID returns a ID for a new span in the trace with traceID. NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } type randomIDGenerator struct { sync.Mutex randSource *rand.Rand } var _ IDGenerator = &randomIDGenerator{} // NewSpanID returns a non-zero span ID from a randomly-chosen sequence. func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID { gen.Lock() defer gen.Unlock() sid := trace.SpanID{} gen.randSource.Read(sid[:]) return sid } // NewIDs returns a non-zero trace ID and a non-zero span ID from a // randomly-chosen sequence. func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) { gen.Lock() defer gen.Unlock() tid := trace.TraceID{} gen.randSource.Read(tid[:]) sid := trace.SpanID{} gen.randSource.Read(sid[:]) return tid, sid } func defaultIDGenerator() IDGenerator { gen := &randomIDGenerator{} var rngSeed int64 _ = binary.Read(crand.Reader, binary.LittleEndian, &rngSeed) gen.randSource = rand.New(rand.NewSource(rngSeed)) return gen } golang-opentelemetry-otel-1.1.0/sdk/trace/link.go000066400000000000000000000022441414226744000217640ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) // Link is the relationship between two Spans. The relationship can be within // the same Trace or across different Traces. type Link struct { // SpanContext of the linked Span. SpanContext trace.SpanContext // Attributes describe the aspects of the link. Attributes []attribute.KeyValue // DroppedAttributeCount is the number of attributes that were not // recorded due to configured limits being reached. DroppedAttributeCount int } golang-opentelemetry-otel-1.1.0/sdk/trace/provider.go000066400000000000000000000232261414226744000226640ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "fmt" "sync" "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) const ( defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" ) // tracerProviderConfig type tracerProviderConfig struct { // processors contains collection of SpanProcessors that are processing pipeline // for spans in the trace signal. // SpanProcessors registered with a TracerProvider and are called at the start // and end of a Span's lifecycle, and are called in the order they are // registered. processors []SpanProcessor // sampler is the default sampler used when creating new spans. sampler Sampler // idGenerator is used to generate all Span and Trace IDs when needed. idGenerator IDGenerator // spanLimits defines the attribute, event, and link limits for spans. spanLimits SpanLimits // resource contains attributes representing an entity that produces telemetry. resource *resource.Resource } type TracerProvider struct { mu sync.Mutex namedTracer map[instrumentation.Library]*tracer spanProcessors atomic.Value sampler Sampler idGenerator IDGenerator spanLimits SpanLimits resource *resource.Resource } var _ trace.TracerProvider = &TracerProvider{} // NewTracerProvider returns a new and configured TracerProvider. // // By default the returned TracerProvider is configured with: // - a ParentBased(AlwaysSample) Sampler // - a random number IDGenerator // - the resource.Default() Resource // - the default SpanLimits. // // The passed opts are used to override these default values and configure the // returned TracerProvider appropriately. func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { o := &tracerProviderConfig{} for _, opt := range opts { opt.apply(o) } ensureValidTracerProviderConfig(o) tp := &TracerProvider{ namedTracer: make(map[instrumentation.Library]*tracer), sampler: o.sampler, idGenerator: o.idGenerator, spanLimits: o.spanLimits, resource: o.resource, } for _, sp := range o.processors { tp.RegisterSpanProcessor(sp) } return tp } // Tracer returns a Tracer with the given name and options. If a Tracer for // the given name and options does not exist it is created, otherwise the // existing Tracer is returned. // // If name is empty, DefaultTracerName is used instead. // // This method is safe to be called concurrently. func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { c := trace.NewTracerConfig(opts...) p.mu.Lock() defer p.mu.Unlock() if name == "" { name = defaultTracerName } il := instrumentation.Library{ Name: name, Version: c.InstrumentationVersion(), SchemaURL: c.SchemaURL(), } t, ok := p.namedTracer[il] if !ok { t = &tracer{ provider: p, instrumentationLibrary: il, } p.namedTracer[il] = t } return t } // RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) { p.mu.Lock() defer p.mu.Unlock() new := spanProcessorStates{} if old, ok := p.spanProcessors.Load().(spanProcessorStates); ok { new = append(new, old...) } newSpanSync := &spanProcessorState{ sp: s, state: &sync.Once{}, } new = append(new, newSpanSync) p.spanProcessors.Store(new) } // UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) { p.mu.Lock() defer p.mu.Unlock() spss := spanProcessorStates{} old, ok := p.spanProcessors.Load().(spanProcessorStates) if !ok || len(old) == 0 { return } spss = append(spss, old...) // stop the span processor if it is started and remove it from the list var stopOnce *spanProcessorState var idx int for i, sps := range spss { if sps.sp == s { stopOnce = sps idx = i } } if stopOnce != nil { stopOnce.state.Do(func() { if err := s.Shutdown(context.Background()); err != nil { otel.Handle(err) } }) } if len(spss) > 1 { copy(spss[idx:], spss[idx+1:]) } spss[len(spss)-1] = nil spss = spss[:len(spss)-1] p.spanProcessors.Store(spss) } // ForceFlush immediately exports all spans that have not yet been exported for // all the registered span processors. func (p *TracerProvider) ForceFlush(ctx context.Context) error { spss, ok := p.spanProcessors.Load().(spanProcessorStates) if !ok { return fmt.Errorf("failed to load span processors") } if len(spss) == 0 { return nil } for _, sps := range spss { select { case <-ctx.Done(): return ctx.Err() default: } if err := sps.sp.ForceFlush(ctx); err != nil { return err } } return nil } // Shutdown shuts down the span processors in the order they were registered. func (p *TracerProvider) Shutdown(ctx context.Context) error { spss, ok := p.spanProcessors.Load().(spanProcessorStates) if !ok { return fmt.Errorf("failed to load span processors") } if len(spss) == 0 { return nil } for _, sps := range spss { select { case <-ctx.Done(): return ctx.Err() default: } var err error sps.state.Do(func() { err = sps.sp.Shutdown(ctx) }) if err != nil { return err } } return nil } type TracerProviderOption interface { apply(*tracerProviderConfig) } type traceProviderOptionFunc func(*tracerProviderConfig) func (fn traceProviderOptionFunc) apply(cfg *tracerProviderConfig) { fn(cfg) } // WithSyncer registers the exporter with the TracerProvider using a // SimpleSpanProcessor. // // This is not recommended for production use. The synchronous nature of the // SimpleSpanProcessor that will wrap the exporter make it good for testing, // debugging, or showing examples of other feature, but it will be slow and // have a high computation resource usage overhead. The WithBatcher option is // recommended for production use instead. func WithSyncer(e SpanExporter) TracerProviderOption { return WithSpanProcessor(NewSimpleSpanProcessor(e)) } // WithBatcher registers the exporter with the TracerProvider using a // BatchSpanProcessor configured with the passed opts. func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption { return WithSpanProcessor(NewBatchSpanProcessor(e, opts...)) } // WithSpanProcessor registers the SpanProcessor with a TracerProvider. func WithSpanProcessor(sp SpanProcessor) TracerProviderOption { return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { cfg.processors = append(cfg.processors, sp) }) } // WithResource returns a TracerProviderOption that will configure the // Resource r as a TracerProvider's Resource. The configured Resource is // referenced by all the Tracers the TracerProvider creates. It represents the // entity producing telemetry. // // If this option is not used, the TracerProvider will use the // resource.Default() Resource by default. func WithResource(r *resource.Resource) TracerProviderOption { return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { var err error cfg.resource, err = resource.Merge(resource.Environment(), r) if err != nil { otel.Handle(err) } }) } // WithIDGenerator returns a TracerProviderOption that will configure the // IDGenerator g as a TracerProvider's IDGenerator. The configured IDGenerator // is used by the Tracers the TracerProvider creates to generate new Span and // Trace IDs. // // If this option is not used, the TracerProvider will use a random number // IDGenerator by default. func WithIDGenerator(g IDGenerator) TracerProviderOption { return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { if g != nil { cfg.idGenerator = g } }) } // WithSampler returns a TracerProviderOption that will configure the Sampler // s as a TracerProvider's Sampler. The configured Sampler is used by the // Tracers the TracerProvider creates to make their sampling decisions for the // Spans they create. // // If this option is not used, the TracerProvider will use a // ParentBased(AlwaysSample) Sampler by default. func WithSampler(s Sampler) TracerProviderOption { return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { if s != nil { cfg.sampler = s } }) } // WithSpanLimits returns a TracerProviderOption that will configure the // SpanLimits sl as a TracerProvider's SpanLimits. The configured SpanLimits // are used used by the Tracers the TracerProvider and the Spans they create // to limit tracing resources used. // // If this option is not used, the TracerProvider will use the default // SpanLimits. func WithSpanLimits(sl SpanLimits) TracerProviderOption { return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { cfg.spanLimits = sl }) } // ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid. func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) { if cfg.sampler == nil { cfg.sampler = ParentBased(AlwaysSample()) } if cfg.idGenerator == nil { cfg.idGenerator = defaultIDGenerator() } cfg.spanLimits.ensureDefault() if cfg.resource == nil { cfg.resource = resource.Default() } } golang-opentelemetry-otel-1.1.0/sdk/trace/provider_test.go000066400000000000000000000050701414226744000237200ustar00rootroot00000000000000// 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 trace import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/trace" ) type basicSpanProcesor struct { running bool injectShutdownError error } func (t *basicSpanProcesor) Shutdown(context.Context) error { t.running = false return t.injectShutdownError } func (t *basicSpanProcesor) OnStart(context.Context, ReadWriteSpan) {} func (t *basicSpanProcesor) OnEnd(ReadOnlySpan) {} func (t *basicSpanProcesor) ForceFlush(context.Context) error { return nil } func TestShutdownTraceProvider(t *testing.T) { stp := NewTracerProvider() sp := &basicSpanProcesor{} stp.RegisterSpanProcessor(sp) sp.running = true _ = stp.Shutdown(context.Background()) if sp.running != false { t.Errorf("Error shutdown basicSpanProcesor\n") } } func TestFailedProcessorShutdown(t *testing.T) { stp := NewTracerProvider() spErr := errors.New("basic span processor shutdown failure") sp := &basicSpanProcesor{ running: true, injectShutdownError: spErr, } stp.RegisterSpanProcessor(sp) err := stp.Shutdown(context.Background()) assert.Error(t, err) assert.Equal(t, err, spErr) } func TestFailedProcessorShutdownInUnregister(t *testing.T) { handler.Reset() stp := NewTracerProvider() spErr := errors.New("basic span processor shutdown failure") sp := &basicSpanProcesor{ running: true, injectShutdownError: spErr, } stp.RegisterSpanProcessor(sp) stp.UnregisterSpanProcessor(sp) assert.Contains(t, handler.errs, spErr) err := stp.Shutdown(context.Background()) assert.NoError(t, err) } func TestSchemaURL(t *testing.T) { stp := NewTracerProvider() schemaURL := "https://opentelemetry.io/schemas/1.2.0" tracerIface := stp.Tracer("tracername", trace.WithSchemaURL(schemaURL)) // Verify that the SchemaURL of the constructed Tracer is correctly populated. tracerStruct := tracerIface.(*tracer) assert.EqualValues(t, schemaURL, tracerStruct.instrumentationLibrary.SchemaURL) } golang-opentelemetry-otel-1.1.0/sdk/trace/sampling.go000066400000000000000000000205331414226744000226420ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "encoding/binary" "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) // Sampler decides whether a trace should be sampled and exported. type Sampler interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // ShouldSample returns a SamplingResult based on a decision made from the // passed parameters. ShouldSample(parameters SamplingParameters) SamplingResult // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Description returns information describing the Sampler. Description() string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // SamplingParameters contains the values passed to a Sampler. type SamplingParameters struct { ParentContext context.Context TraceID trace.TraceID Name string Kind trace.SpanKind Attributes []attribute.KeyValue Links []trace.Link } // SamplingDecision indicates whether a span is dropped, recorded and/or sampled. type SamplingDecision uint8 // Valid sampling decisions const ( // Drop will not record the span and all attributes/events will be dropped Drop SamplingDecision = iota // Record indicates the span's `IsRecording() == true`, but `Sampled` flag // *must not* be set RecordOnly // RecordAndSample has span's `IsRecording() == true` and `Sampled` flag // *must* be set RecordAndSample ) // SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate. type SamplingResult struct { Decision SamplingDecision Attributes []attribute.KeyValue Tracestate trace.TraceState } type traceIDRatioSampler struct { traceIDUpperBound uint64 description string } func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult { psc := trace.SpanContextFromContext(p.ParentContext) x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1 if x < ts.traceIDUpperBound { return SamplingResult{ Decision: RecordAndSample, Tracestate: psc.TraceState(), } } return SamplingResult{ Decision: Drop, Tracestate: psc.TraceState(), } } func (ts traceIDRatioSampler) Description() string { return ts.description } // TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will // always sample. Fractions < 0 are treated as zero. To respect the // parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used // as a delegate of a `Parent` sampler. //nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased` func TraceIDRatioBased(fraction float64) Sampler { if fraction >= 1 { return AlwaysSample() } if fraction <= 0 { fraction = 0 } return &traceIDRatioSampler{ traceIDUpperBound: uint64(fraction * (1 << 63)), description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction), } } type alwaysOnSampler struct{} func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult { return SamplingResult{ Decision: RecordAndSample, Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), } } func (as alwaysOnSampler) Description() string { return "AlwaysOnSampler" } // AlwaysSample returns a Sampler that samples every trace. // Be careful about using this sampler in a production application with // significant traffic: a new trace will be started and exported for every // request. func AlwaysSample() Sampler { return alwaysOnSampler{} } type alwaysOffSampler struct{} func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult { return SamplingResult{ Decision: Drop, Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), } } func (as alwaysOffSampler) Description() string { return "AlwaysOffSampler" } // NeverSample returns a Sampler that samples no traces. func NeverSample() Sampler { return alwaysOffSampler{} } // ParentBased returns a composite sampler which behaves differently, // based on the parent of the span. If the span has no parent, // the root(Sampler) is used to make sampling decision. If the span has // a parent, depending on whether the parent is remote and whether it // is sampled, one of the following samplers will apply: // - remoteParentSampled(Sampler) (default: AlwaysOn) // - remoteParentNotSampled(Sampler) (default: AlwaysOff) // - localParentSampled(Sampler) (default: AlwaysOn) // - localParentNotSampled(Sampler) (default: AlwaysOff) func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler { return parentBased{ root: root, config: configureSamplersForParentBased(samplers), } } type parentBased struct { root Sampler config samplerConfig } func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig { c := samplerConfig{ remoteParentSampled: AlwaysSample(), remoteParentNotSampled: NeverSample(), localParentSampled: AlwaysSample(), localParentNotSampled: NeverSample(), } for _, so := range samplers { so.apply(&c) } return c } // samplerConfig is a group of options for parentBased sampler. type samplerConfig struct { remoteParentSampled, remoteParentNotSampled Sampler localParentSampled, localParentNotSampled Sampler } // ParentBasedSamplerOption configures the sampler for a particular sampling case. type ParentBasedSamplerOption interface { apply(*samplerConfig) } // WithRemoteParentSampled sets the sampler for the case of sampled remote parent. func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption { return remoteParentSampledOption{s} } type remoteParentSampledOption struct { s Sampler } func (o remoteParentSampledOption) apply(config *samplerConfig) { config.remoteParentSampled = o.s } // WithRemoteParentNotSampled sets the sampler for the case of remote parent // which is not sampled. func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption { return remoteParentNotSampledOption{s} } type remoteParentNotSampledOption struct { s Sampler } func (o remoteParentNotSampledOption) apply(config *samplerConfig) { config.remoteParentNotSampled = o.s } // WithLocalParentSampled sets the sampler for the case of sampled local parent. func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption { return localParentSampledOption{s} } type localParentSampledOption struct { s Sampler } func (o localParentSampledOption) apply(config *samplerConfig) { config.localParentSampled = o.s } // WithLocalParentNotSampled sets the sampler for the case of local parent // which is not sampled. func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption { return localParentNotSampledOption{s} } type localParentNotSampledOption struct { s Sampler } func (o localParentNotSampledOption) apply(config *samplerConfig) { config.localParentNotSampled = o.s } func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult { psc := trace.SpanContextFromContext(p.ParentContext) if psc.IsValid() { if psc.IsRemote() { if psc.IsSampled() { return pb.config.remoteParentSampled.ShouldSample(p) } return pb.config.remoteParentNotSampled.ShouldSample(p) } if psc.IsSampled() { return pb.config.localParentSampled.ShouldSample(p) } return pb.config.localParentNotSampled.ShouldSample(p) } return pb.root.ShouldSample(p) } func (pb parentBased) Description() string { return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+ "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", pb.root.Description(), pb.config.remoteParentSampled.Description(), pb.config.remoteParentNotSampled.Description(), pb.config.localParentSampled.Description(), pb.config.localParentNotSampled.Description(), ) } golang-opentelemetry-otel-1.1.0/sdk/trace/sampling_test.go000066400000000000000000000147271414226744000237110ustar00rootroot00000000000000// 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 trace import ( "context" "fmt" "math/rand" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace" ) func TestParentBasedDefaultLocalParentSampled(t *testing.T) { sampler := ParentBased(AlwaysSample()) traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") parentCtx := trace.ContextWithSpanContext( context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, }), ) if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != RecordAndSample { t.Error("Sampling decision should be RecordAndSample") } } func TestParentBasedDefaultLocalParentNotSampled(t *testing.T) { sampler := ParentBased(AlwaysSample()) traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") parentCtx := trace.ContextWithSpanContext( context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, }), ) if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != Drop { t.Error("Sampling decision should be Drop") } } func TestParentBasedWithNoParent(t *testing.T) { params := SamplingParameters{} sampler := ParentBased(AlwaysSample()) if sampler.ShouldSample(params).Decision != RecordAndSample { t.Error("Sampling decision should be RecordAndSample") } sampler = ParentBased(NeverSample()) if sampler.ShouldSample(params).Decision != Drop { t.Error("Sampling decision should be Drop") } } func TestParentBasedWithSamplerOptions(t *testing.T) { testCases := []struct { name string samplerOption ParentBasedSamplerOption isParentRemote, isParentSampled bool expectedDecision SamplingDecision }{ { "localParentSampled", WithLocalParentSampled(NeverSample()), false, true, Drop, }, { "localParentNotSampled", WithLocalParentNotSampled(AlwaysSample()), false, false, RecordAndSample, }, { "remoteParentSampled", WithRemoteParentSampled(NeverSample()), true, true, Drop, }, { "remoteParentNotSampled", WithRemoteParentNotSampled(AlwaysSample()), true, false, RecordAndSample, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") pscc := trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, Remote: tc.isParentRemote, } if tc.isParentSampled { pscc.TraceFlags = trace.FlagsSampled } params := SamplingParameters{ ParentContext: trace.ContextWithSpanContext( context.Background(), trace.NewSpanContext(pscc), ), } sampler := ParentBased( nil, tc.samplerOption, ) var wantStr, gotStr string switch tc.expectedDecision { case RecordAndSample: wantStr = "RecordAndSample" case Drop: wantStr = "Drop" default: wantStr = "unknown" } actualDecision := sampler.ShouldSample(params).Decision switch actualDecision { case RecordAndSample: gotStr = "RecordAndSample" case Drop: gotStr = "Drop" default: gotStr = "unknown" } assert.Equalf(t, tc.expectedDecision, actualDecision, "want %s, got %s", wantStr, gotStr) }) } } func TestParentBasedDefaultDescription(t *testing.T) { sampler := ParentBased(AlwaysSample()) expectedDescription := fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+ "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", AlwaysSample().Description(), AlwaysSample().Description(), NeverSample().Description(), AlwaysSample().Description(), NeverSample().Description()) if sampler.Description() != expectedDescription { t.Errorf("Sampler description should be %s, got '%s' instead", expectedDescription, sampler.Description(), ) } } // TraceIDRatioBased sampler requirements state // "A TraceIDRatioBased sampler with a given sampling rate MUST also sample // all traces that any TraceIDRatioBased sampler with a lower sampling rate // would sample." func TestTraceIdRatioSamplesInclusively(t *testing.T) { const ( numSamplers = 1000 numTraces = 100 ) idg := defaultIDGenerator() for i := 0; i < numSamplers; i++ { ratioLo, ratioHi := rand.Float64(), rand.Float64() if ratioHi < ratioLo { ratioLo, ratioHi = ratioHi, ratioLo } samplerHi := TraceIDRatioBased(ratioHi) samplerLo := TraceIDRatioBased(ratioLo) for j := 0; j < numTraces; j++ { traceID, _ := idg.NewIDs(context.Background()) params := SamplingParameters{TraceID: traceID} if samplerLo.ShouldSample(params).Decision == RecordAndSample { require.Equal(t, RecordAndSample, samplerHi.ShouldSample(params).Decision, "%s sampled but %s did not", samplerLo.Description(), samplerHi.Description()) } } } } func TestTracestateIsPassed(t *testing.T) { testCases := []struct { name string sampler Sampler }{ { "notSampled", NeverSample(), }, { "sampled", AlwaysSample(), }, { "parentSampled", ParentBased(AlwaysSample()), }, { "parentNotSampled", ParentBased(NeverSample()), }, { "traceIDRatioSampler", TraceIDRatioBased(.5), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { traceState, err := trace.ParseTraceState("k=v") if err != nil { t.Error(err) } params := SamplingParameters{ ParentContext: trace.ContextWithSpanContext( context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ TraceState: traceState, }), ), } require.Equal(t, traceState, tc.sampler.ShouldSample(params).Tracestate, "TraceState is not equal") }) } } golang-opentelemetry-otel-1.1.0/sdk/trace/simple_span_processor.go000066400000000000000000000100201414226744000254270ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "sync" "go.opentelemetry.io/otel" ) // simpleSpanProcessor is a SpanProcessor that synchronously sends all // completed Spans to a trace.Exporter immediately. type simpleSpanProcessor struct { exporterMu sync.RWMutex exporter SpanExporter stopOnce sync.Once } var _ SpanProcessor = (*simpleSpanProcessor)(nil) // NewSimpleSpanProcessor returns a new SpanProcessor that will synchronously // send completed spans to the exporter immediately. // // This SpanProcessor is not recommended for production use. The synchronous // nature of this SpanProcessor make it good for testing, debugging, or // showing examples of other feature, but it will be slow and have a high // computation resource usage overhead. The BatchSpanProcessor is recommended // for production use instead. func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ exporter: exporter, } return ssp } // OnStart does nothing. func (ssp *simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} // OnEnd immediately exports a ReadOnlySpan. func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { ssp.exporterMu.RLock() defer ssp.exporterMu.RUnlock() if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { otel.Handle(err) } } } // Shutdown shuts down the exporter this SimpleSpanProcessor exports to. func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error { var err error ssp.stopOnce.Do(func() { stopFunc := func(exp SpanExporter) (<-chan error, func()) { done := make(chan error) return done, func() { done <- exp.Shutdown(ctx) } } // The exporter field of the simpleSpanProcessor needs to be zeroed to // signal it is shut down, meaning all subsequent calls to OnEnd will // be gracefully ignored. This needs to be done synchronously to avoid // any race condition. // // A closure is used to keep reference to the exporter and then the // field is zeroed. This ensures the simpleSpanProcessor is shut down // before the exporter. This order is important as it avoids a // potential deadlock. If the exporter shut down operation generates a // span, that span would need to be exported. Meaning, OnEnd would be // called and try acquiring the lock that is held here. ssp.exporterMu.Lock() done, shutdown := stopFunc(ssp.exporter) ssp.exporter = nil ssp.exporterMu.Unlock() go shutdown() // Wait for the exporter to shut down or the deadline to expire. select { case err = <-done: case <-ctx.Done(): // It is possible for the exporter to have immediately shut down // and the context to be done simultaneously. In that case this // outer select statement will randomly choose a case. This will // result in a different returned error for similar scenarios. // Instead, double check if the exporter shut down at the same // time and return that error if so. This will ensure consistency // as well as ensure the caller knows the exporter shut down // successfully (they can already determine if the deadline is // expired given they passed the context). select { case err = <-done: default: err = ctx.Err() } } }) return err } // ForceFlush does nothing as there is no data to flush. func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { return nil } golang-opentelemetry-otel-1.1.0/sdk/trace/simple_span_processor_test.go000066400000000000000000000110711414226744000264750ustar00rootroot00000000000000// 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 trace_test import ( "context" "errors" "testing" "time" "go.opentelemetry.io/otel/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) var ( tid, _ = trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ = trace.SpanIDFromHex("0102040810203040") ) type testExporter struct { spans []sdktrace.ReadOnlySpan shutdown bool } func (t *testExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { t.spans = append(t.spans, spans...) return nil } func (t *testExporter) Shutdown(ctx context.Context) error { t.shutdown = true select { case <-ctx.Done(): // Ensure context deadline tests receive the expected error. return ctx.Err() default: return nil } } var _ sdktrace.SpanExporter = (*testExporter)(nil) func TestNewSimpleSpanProcessor(t *testing.T) { if ssp := sdktrace.NewSimpleSpanProcessor(&testExporter{}); ssp == nil { t.Error("failed to create new SimpleSpanProcessor") } } func TestNewSimpleSpanProcessorWithNilExporter(t *testing.T) { if ssp := sdktrace.NewSimpleSpanProcessor(nil); ssp == nil { t.Error("failed to create new SimpleSpanProcessor with nil exporter") } } func startSpan(tp trace.TracerProvider) trace.Span { tr := tp.Tracer("SimpleSpanProcessor") sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) _, span := tr.Start(ctx, "OnEnd") return span } func TestSimpleSpanProcessorOnEnd(t *testing.T) { tp := basicTracerProvider(t) te := testExporter{} ssp := sdktrace.NewSimpleSpanProcessor(&te) tp.RegisterSpanProcessor(ssp) startSpan(tp).End() wantTraceID := tid gotTraceID := te.spans[0].SpanContext().TraceID() if wantTraceID != gotTraceID { t.Errorf("SimplerSpanProcessor OnEnd() check: got %+v, want %+v\n", gotTraceID, wantTraceID) } } func TestSimpleSpanProcessorShutdown(t *testing.T) { exporter := &testExporter{} ssp := sdktrace.NewSimpleSpanProcessor(exporter) // Ensure we can export a span before we test we cannot after shutdown. tp := basicTracerProvider(t) tp.RegisterSpanProcessor(ssp) startSpan(tp).End() nExported := len(exporter.spans) if nExported != 1 { t.Error("failed to verify span export") } if err := ssp.Shutdown(context.Background()); err != nil { t.Errorf("shutting the SimpleSpanProcessor down: %v", err) } if !exporter.shutdown { t.Error("SimpleSpanProcessor.Shutdown did not shut down exporter") } startSpan(tp).End() if len(exporter.spans) > nExported { t.Error("exported span to shutdown exporter") } } func TestSimpleSpanProcessorShutdownOnEndConcurrency(t *testing.T) { exporter := &testExporter{} ssp := sdktrace.NewSimpleSpanProcessor(exporter) tp := basicTracerProvider(t) tp.RegisterSpanProcessor(ssp) stop := make(chan struct{}) done := make(chan struct{}) go func() { defer func() { done <- struct{}{} }() for { select { case <-stop: return default: startSpan(tp).End() } } }() if err := ssp.Shutdown(context.Background()); err != nil { t.Errorf("shutting the SimpleSpanProcessor down: %v", err) } if !exporter.shutdown { t.Error("SimpleSpanProcessor.Shutdown did not shut down exporter") } stop <- struct{}{} <-done } func TestSimpleSpanProcessorShutdownHonorsContextDeadline(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) defer cancel() <-ctx.Done() ssp := sdktrace.NewSimpleSpanProcessor(&testExporter{}) if got, want := ssp.Shutdown(ctx), context.DeadlineExceeded; !errors.Is(got, want) { t.Errorf("SimpleSpanProcessor.Shutdown did not return %v, got %v", want, got) } } func TestSimpleSpanProcessorShutdownHonorsContextCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() ssp := sdktrace.NewSimpleSpanProcessor(&testExporter{}) if got, want := ssp.Shutdown(ctx), context.Canceled; !errors.Is(got, want) { t.Errorf("SimpleSpanProcessor.Shutdown did not return %v, got %v", want, got) } } golang-opentelemetry-otel-1.1.0/sdk/trace/snapshot.go000066400000000000000000000076751414226744000227030ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/trace" ) // snapshot is an record of a spans state at a particular checkpointed time. // It is used as a read-only representation of that state. type snapshot struct { name string spanContext trace.SpanContext parent trace.SpanContext spanKind trace.SpanKind startTime time.Time endTime time.Time attributes []attribute.KeyValue events []Event links []Link status Status childSpanCount int droppedAttributeCount int droppedEventCount int droppedLinkCount int resource *resource.Resource instrumentationLibrary instrumentation.Library } var _ ReadOnlySpan = snapshot{} func (s snapshot) private() {} // Name returns the name of the span. func (s snapshot) Name() string { return s.name } // SpanContext returns the unique SpanContext that identifies the span. func (s snapshot) SpanContext() trace.SpanContext { return s.spanContext } // Parent returns the unique SpanContext that identifies the parent of the // span if one exists. If the span has no parent the returned SpanContext // will be invalid. func (s snapshot) Parent() trace.SpanContext { return s.parent } // SpanKind returns the role the span plays in a Trace. func (s snapshot) SpanKind() trace.SpanKind { return s.spanKind } // StartTime returns the time the span started recording. func (s snapshot) StartTime() time.Time { return s.startTime } // EndTime returns the time the span stopped recording. It will be zero if // the span has not ended. func (s snapshot) EndTime() time.Time { return s.endTime } // Attributes returns the defining attributes of the span. func (s snapshot) Attributes() []attribute.KeyValue { return s.attributes } // Links returns all the links the span has to other spans. func (s snapshot) Links() []Link { return s.links } // Events returns all the events that occurred within in the spans // lifetime. func (s snapshot) Events() []Event { return s.events } // Status returns the spans status. func (s snapshot) Status() Status { return s.status } // InstrumentationLibrary returns information about the instrumentation // library that created the span. func (s snapshot) InstrumentationLibrary() instrumentation.Library { return s.instrumentationLibrary } // Resource returns information about the entity that produced the span. func (s snapshot) Resource() *resource.Resource { return s.resource } // DroppedAttributes returns the number of attributes dropped by the span // due to limits being reached. func (s snapshot) DroppedAttributes() int { return s.droppedAttributeCount } // DroppedLinks returns the number of links dropped by the span due to limits // being reached. func (s snapshot) DroppedLinks() int { return s.droppedLinkCount } // DroppedEvents returns the number of events dropped by the span due to // limits being reached. func (s snapshot) DroppedEvents() int { return s.droppedEventCount } // ChildSpanCount returns the count of spans that consider the span a // direct parent. func (s snapshot) ChildSpanCount() int { return s.childSpanCount } golang-opentelemetry-otel-1.1.0/sdk/trace/span.go000066400000000000000000000450261414226744000217750ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "fmt" "reflect" "runtime" rt "runtime/trace" "sync" "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/internal" "go.opentelemetry.io/otel/sdk/resource" ) // ReadOnlySpan allows reading information from the data structure underlying a // trace.Span. It is used in places where reading information from a span is // necessary but changing the span isn't necessary or allowed. // // Warning: methods may be added to this interface in minor releases. type ReadOnlySpan interface { // Name returns the name of the span. Name() string // SpanContext returns the unique SpanContext that identifies the span. SpanContext() trace.SpanContext // Parent returns the unique SpanContext that identifies the parent of the // span if one exists. If the span has no parent the returned SpanContext // will be invalid. Parent() trace.SpanContext // SpanKind returns the role the span plays in a Trace. SpanKind() trace.SpanKind // StartTime returns the time the span started recording. StartTime() time.Time // EndTime returns the time the span stopped recording. It will be zero if // the span has not ended. EndTime() time.Time // Attributes returns the defining attributes of the span. Attributes() []attribute.KeyValue // Links returns all the links the span has to other spans. Links() []Link // Events returns all the events that occurred within in the spans // lifetime. Events() []Event // Status returns the spans status. Status() Status // InstrumentationLibrary returns information about the instrumentation // library that created the span. InstrumentationLibrary() instrumentation.Library // Resource returns information about the entity that produced the span. Resource() *resource.Resource // DroppedAttributes returns the number of attributes dropped by the span // due to limits being reached. DroppedAttributes() int // DroppedLinks returns the number of links dropped by the span due to // limits being reached. DroppedLinks() int // DroppedEvents returns the number of events dropped by the span due to // limits being reached. DroppedEvents() int // ChildSpanCount returns the count of spans that consider the span a // direct parent. ChildSpanCount() int // A private method to prevent users implementing the // interface and so future additions to it will not // violate compatibility. private() } // ReadWriteSpan exposes the same methods as trace.Span and in addition allows // reading information from the underlying data structure. // This interface exposes the union of the methods of trace.Span (which is a // "write-only" span) and ReadOnlySpan. New methods for writing or reading span // information should be added under trace.Span or ReadOnlySpan, respectively. // // Warning: methods may be added to this interface in minor releases. type ReadWriteSpan interface { trace.Span ReadOnlySpan } // recordingSpan is an implementation of the OpenTelemetry Span API // representing the individual component of a trace that is sampled. type recordingSpan struct { // mu protects the contents of this span. mu sync.Mutex // parent holds the parent span of this span as a trace.SpanContext. parent trace.SpanContext // spanKind represents the kind of this span as a trace.SpanKind. spanKind trace.SpanKind // name is the name of this span. name string // startTime is the time at which this span was started. startTime time.Time // endTime is the time at which this span was ended. It contains the zero // value of time.Time until the span is ended. endTime time.Time // status is the status of this span. status Status // childSpanCount holds the number of child spans created for this span. childSpanCount int // resource contains attributes representing an entity that produced this // span. resource *resource.Resource // instrumentationLibrary defines the instrumentation library used to // provide instrumentation. instrumentationLibrary instrumentation.Library // spanContext holds the SpanContext of this span. spanContext trace.SpanContext // attributes are capped at configured limit. When the capacity is reached // an oldest entry is removed to create room for a new entry. attributes *attributesMap // events are stored in FIFO queue capped by configured limit. events *evictedQueue // links are stored in FIFO queue capped by configured limit. links *evictedQueue // executionTracerTaskEnd ends the execution tracer span. executionTracerTaskEnd func() // tracer is the SDK tracer that created this span. tracer *tracer // spanLimits holds the limits to this span. spanLimits SpanLimits } var _ ReadWriteSpan = (*recordingSpan)(nil) var _ runtimeTracer = (*recordingSpan)(nil) // SpanContext returns the SpanContext of this span. func (s *recordingSpan) SpanContext() trace.SpanContext { if s == nil { return trace.SpanContext{} } return s.spanContext } // IsRecording returns if this span is being recorded. If this span has ended // this will return false. func (s *recordingSpan) IsRecording() bool { if s == nil { return false } s.mu.Lock() defer s.mu.Unlock() return s.endTime.IsZero() } // SetStatus sets the status of the Span in the form of a code and a // description, overriding previous values set. The description is only // included in the set status when the code is for an error. If this span is // not being recorded than this method does nothing. func (s *recordingSpan) SetStatus(code codes.Code, description string) { if !s.IsRecording() { return } status := Status{Code: code} if code == codes.Error { status.Description = description } s.mu.Lock() s.status = status s.mu.Unlock() } // SetAttributes sets attributes of this span. // // If a key from attributes already exists the value associated with that key // will be overwritten with the value contained in attributes. // // If this span is not being recorded than this method does nothing. func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { if !s.IsRecording() { return } s.copyToCappedAttributes(attributes...) } // End ends the span. This method does nothing if the span is already ended or // is not being recorded. // // The only SpanOption currently supported is WithTimestamp which will set the // end time for a Span's life-cycle. // // If this method is called while panicking an error event is added to the // Span before ending it and the panic is continued. func (s *recordingSpan) End(options ...trace.SpanEndOption) { // Do not start by checking if the span is being recorded which requires // acquiring a lock. Make a minimal check that the span is not nil. if s == nil { return } // Store the end time as soon as possible to avoid artificially increasing // the span's duration in case some operation below takes a while. et := internal.MonotonicEndTime(s.startTime) // Do relative expensive check now that we have an end time and see if we // need to do any more processing. if !s.IsRecording() { return } config := trace.NewSpanEndConfig(options...) if recovered := recover(); recovered != nil { // Record but don't stop the panic. defer panic(recovered) opts := []trace.EventOption{ trace.WithAttributes( semconv.ExceptionTypeKey.String(typeStr(recovered)), semconv.ExceptionMessageKey.String(fmt.Sprint(recovered)), ), } if config.StackTrace() { opts = append(opts, trace.WithAttributes( semconv.ExceptionStacktraceKey.String(recordStackTrace()), )) } s.addEvent(semconv.ExceptionEventName, opts...) } if s.executionTracerTaskEnd != nil { s.executionTracerTaskEnd() } s.mu.Lock() // Setting endTime to non-zero marks the span as ended and not recording. if config.Timestamp().IsZero() { s.endTime = et } else { s.endTime = config.Timestamp() } s.mu.Unlock() if sps, ok := s.tracer.provider.spanProcessors.Load().(spanProcessorStates); ok { if len(sps) == 0 { return } snap := s.snapshot() for _, sp := range sps { sp.sp.OnEnd(snap) } } } // RecordError will record err as a span event for this span. An additional call to // SetStatus is required if the Status of the Span should be set to Error, this method // does not change the Span status. If this span is not being recorded or err is nil // than this method does nothing. func (s *recordingSpan) RecordError(err error, opts ...trace.EventOption) { if s == nil || err == nil || !s.IsRecording() { return } opts = append(opts, trace.WithAttributes( semconv.ExceptionTypeKey.String(typeStr(err)), semconv.ExceptionMessageKey.String(err.Error()), )) c := trace.NewEventConfig(opts...) if c.StackTrace() { opts = append(opts, trace.WithAttributes( semconv.ExceptionStacktraceKey.String(recordStackTrace()), )) } s.addEvent(semconv.ExceptionEventName, opts...) } func typeStr(i interface{}) string { t := reflect.TypeOf(i) if t.PkgPath() == "" && t.Name() == "" { // Likely a builtin type. return t.String() } return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) } func recordStackTrace() string { stackTrace := make([]byte, 2048) n := runtime.Stack(stackTrace, false) return string(stackTrace[0:n]) } // AddEvent adds an event with the provided name and options. If this span is // not being recorded than this method does nothing. func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) { if !s.IsRecording() { return } s.addEvent(name, o...) } func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) { c := trace.NewEventConfig(o...) // Discard over limited attributes attributes := c.Attributes() var discarded int if len(attributes) > s.spanLimits.AttributePerEventCountLimit { discarded = len(attributes) - s.spanLimits.AttributePerEventCountLimit attributes = attributes[:s.spanLimits.AttributePerEventCountLimit] } s.mu.Lock() defer s.mu.Unlock() s.events.add(Event{ Name: name, Attributes: attributes, DroppedAttributeCount: discarded, Time: c.Timestamp(), }) } // SetName sets the name of this span. If this span is not being recorded than // this method does nothing. func (s *recordingSpan) SetName(name string) { if !s.IsRecording() { return } s.mu.Lock() defer s.mu.Unlock() s.name = name } // Name returns the name of this span. func (s *recordingSpan) Name() string { s.mu.Lock() defer s.mu.Unlock() return s.name } // Name returns the SpanContext of this span's parent span. func (s *recordingSpan) Parent() trace.SpanContext { s.mu.Lock() defer s.mu.Unlock() return s.parent } // SpanKind returns the SpanKind of this span. func (s *recordingSpan) SpanKind() trace.SpanKind { s.mu.Lock() defer s.mu.Unlock() return s.spanKind } // StartTime returns the time this span started. func (s *recordingSpan) StartTime() time.Time { s.mu.Lock() defer s.mu.Unlock() return s.startTime } // EndTime returns the time this span ended. For spans that have not yet // ended, the returned value will be the zero value of time.Time. func (s *recordingSpan) EndTime() time.Time { s.mu.Lock() defer s.mu.Unlock() return s.endTime } // Attributes returns the attributes of this span. func (s *recordingSpan) Attributes() []attribute.KeyValue { s.mu.Lock() defer s.mu.Unlock() if s.attributes.evictList.Len() == 0 { return []attribute.KeyValue{} } return s.attributes.toKeyValue() } // Links returns the links of this span. func (s *recordingSpan) Links() []Link { s.mu.Lock() defer s.mu.Unlock() if len(s.links.queue) == 0 { return []Link{} } return s.interfaceArrayToLinksArray() } // Events returns the events of this span. func (s *recordingSpan) Events() []Event { s.mu.Lock() defer s.mu.Unlock() if len(s.events.queue) == 0 { return []Event{} } return s.interfaceArrayToEventArray() } // Status returns the status of this span. func (s *recordingSpan) Status() Status { s.mu.Lock() defer s.mu.Unlock() return s.status } // InstrumentationLibrary returns the instrumentation.Library associated with // the Tracer that created this span. func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library { s.mu.Lock() defer s.mu.Unlock() return s.instrumentationLibrary } // Resource returns the Resource associated with the Tracer that created this // span. func (s *recordingSpan) Resource() *resource.Resource { s.mu.Lock() defer s.mu.Unlock() return s.resource } func (s *recordingSpan) addLink(link trace.Link) { if !s.IsRecording() || !link.SpanContext.IsValid() { return } s.mu.Lock() defer s.mu.Unlock() var droppedAttributeCount int // Discard over limited attributes if len(link.Attributes) > s.spanLimits.AttributePerLinkCountLimit { droppedAttributeCount = len(link.Attributes) - s.spanLimits.AttributePerLinkCountLimit link.Attributes = link.Attributes[:s.spanLimits.AttributePerLinkCountLimit] } s.links.add(Link{link.SpanContext, link.Attributes, droppedAttributeCount}) } // DroppedAttributes returns the number of attributes dropped by the span // due to limits being reached. func (s *recordingSpan) DroppedAttributes() int { s.mu.Lock() defer s.mu.Unlock() return s.attributes.droppedCount } // DroppedLinks returns the number of links dropped by the span due to limits // being reached. func (s *recordingSpan) DroppedLinks() int { s.mu.Lock() defer s.mu.Unlock() return s.links.droppedCount } // DroppedEvents returns the number of events dropped by the span due to // limits being reached. func (s *recordingSpan) DroppedEvents() int { s.mu.Lock() defer s.mu.Unlock() return s.events.droppedCount } // ChildSpanCount returns the count of spans that consider the span a // direct parent. func (s *recordingSpan) ChildSpanCount() int { s.mu.Lock() defer s.mu.Unlock() return s.childSpanCount } // TracerProvider returns a trace.TracerProvider that can be used to generate // additional Spans on the same telemetry pipeline as the current Span. func (s *recordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } // snapshot creates a read-only copy of the current state of the span. func (s *recordingSpan) snapshot() ReadOnlySpan { var sd snapshot s.mu.Lock() defer s.mu.Unlock() sd.endTime = s.endTime sd.instrumentationLibrary = s.instrumentationLibrary sd.name = s.name sd.parent = s.parent sd.resource = s.resource sd.spanContext = s.spanContext sd.spanKind = s.spanKind sd.startTime = s.startTime sd.status = s.status sd.childSpanCount = s.childSpanCount if s.attributes.evictList.Len() > 0 { sd.attributes = s.attributes.toKeyValue() sd.droppedAttributeCount = s.attributes.droppedCount } if len(s.events.queue) > 0 { sd.events = s.interfaceArrayToEventArray() sd.droppedEventCount = s.events.droppedCount } if len(s.links.queue) > 0 { sd.links = s.interfaceArrayToLinksArray() sd.droppedLinkCount = s.links.droppedCount } return &sd } func (s *recordingSpan) interfaceArrayToLinksArray() []Link { linkArr := make([]Link, 0) for _, value := range s.links.queue { linkArr = append(linkArr, value.(Link)) } return linkArr } func (s *recordingSpan) interfaceArrayToEventArray() []Event { eventArr := make([]Event, 0) for _, value := range s.events.queue { eventArr = append(eventArr, value.(Event)) } return eventArr } func (s *recordingSpan) copyToCappedAttributes(attributes ...attribute.KeyValue) { s.mu.Lock() defer s.mu.Unlock() for _, a := range attributes { // Ensure attributes conform to the specification: // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/common/common.md#attributes if a.Valid() { s.attributes.add(a) } } } func (s *recordingSpan) addChild() { if !s.IsRecording() { return } s.mu.Lock() s.childSpanCount++ s.mu.Unlock() } func (*recordingSpan) private() {} // runtimeTrace starts a "runtime/trace".Task for the span and returns a // context containing the task. func (s *recordingSpan) runtimeTrace(ctx context.Context) context.Context { if !rt.IsEnabled() { // Avoid additional overhead if runtime/trace is not enabled. return ctx } nctx, task := rt.NewTask(ctx, s.name) s.mu.Lock() s.executionTracerTaskEnd = task.End s.mu.Unlock() return nctx } // nonRecordingSpan is a minimal implementation of the OpenTelemetry Span API // that wraps a SpanContext. It performs no operations other than to return // the wrapped SpanContext or TracerProvider that created it. type nonRecordingSpan struct { // tracer is the SDK tracer that created this span. tracer *tracer sc trace.SpanContext } var _ trace.Span = nonRecordingSpan{} // SpanContext returns the wrapped SpanContext. func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc } // IsRecording always returns false. func (nonRecordingSpan) IsRecording() bool { return false } // SetStatus does nothing. func (nonRecordingSpan) SetStatus(codes.Code, string) {} // SetError does nothing. func (nonRecordingSpan) SetError(bool) {} // SetAttributes does nothing. func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} // End does nothing. func (nonRecordingSpan) End(...trace.SpanEndOption) {} // RecordError does nothing. func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} // AddEvent does nothing. func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {} // SetName does nothing. func (nonRecordingSpan) SetName(string) {} // TracerProvider returns the trace.TracerProvider that provided the Tracer // that created this span. func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } func isRecording(s SamplingResult) bool { return s.Decision == RecordOnly || s.Decision == RecordAndSample } func isSampled(s SamplingResult) bool { return s.Decision == RecordAndSample } // Status is the classified state of a Span. type Status struct { // Code is an identifier of a Spans state classification. Code codes.Code // Description is a user hint about why that status was set. It is only // applicable when Code is Error. Description string } golang-opentelemetry-otel-1.1.0/sdk/trace/span_exporter.go000066400000000000000000000041071414226744000237200ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import "context" // SpanExporter handles the delivery of spans to external receivers. This is // the final component in the trace export pipeline. type SpanExporter interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // ExportSpans exports a batch of spans. // // This function is called synchronously, so there is no concurrency // safety requirement. However, due to the synchronous calling pattern, // it is critical that all timeouts and cancellations contained in the // passed context must be honored. // // Any retry logic must be contained in this function. The SDK that // calls this function will not implement any retry logic. All errors // returned by this function are considered unrecoverable and will be // reported to a configured error Handler. ExportSpans(ctx context.Context, spans []ReadOnlySpan) error // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Shutdown notifies the exporter of a pending halt to operations. The // exporter is expected to preform any cleanup or synchronization it // requires while honoring all timeouts and cancellations contained in // the passed context. Shutdown(ctx context.Context) error // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } golang-opentelemetry-otel-1.1.0/sdk/trace/span_processor.go000066400000000000000000000052461414226744000240740ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "sync" ) // SpanProcessor is a processing pipeline for spans in the trace signal. // SpanProcessors registered with a TracerProvider and are called at the start // and end of a Span's lifecycle, and are called in the order they are // registered. type SpanProcessor interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // OnStart is called when a span is started. It is called synchronously // and should not block. OnStart(parent context.Context, s ReadWriteSpan) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // OnEnd is called when span is finished. It is called synchronously and // hence not block. OnEnd(s ReadOnlySpan) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Shutdown is called when the SDK shuts down. Any cleanup or release of // resources held by the processor should be done in this call. // // Calls to OnStart, OnEnd, or ForceFlush after this has been called // should be ignored. // // All timeouts and cancellations contained in ctx must be honored, this // should not block indefinitely. Shutdown(ctx context.Context) error // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // ForceFlush exports all ended spans to the configured Exporter that have not yet // been exported. It should only be called when absolutely necessary, such as when // using a FaaS provider that may suspend the process after an invocation, but before // the Processor can export the completed spans. ForceFlush(ctx context.Context) error // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } type spanProcessorState struct { sp SpanProcessor state *sync.Once } type spanProcessorStates []*spanProcessorState golang-opentelemetry-otel-1.1.0/sdk/trace/span_processor_annotator_example_test.go000066400000000000000000000054401414226744000307270ustar00rootroot00000000000000// 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 trace import ( "context" "fmt" "go.opentelemetry.io/otel/attribute" ) /* Sometimes information about a runtime environment can change dynamically or be delayed from startup. Instead of continuously recreating and distributing a TracerProvider with an immutable Resource or delaying the startup of your application on a slow-loading piece of information, annotate the created spans dynamically using a SpanProcessor. */ var ( // owner represents the owner of the application. In this example it is // stored as a simple string, but in real-world use this may be the // response to an asynchronous request. owner = "unknown" ownerKey = attribute.Key("owner") ) // Annotator is a SpanProcessor that adds attributes to all started spans. type Annotator struct { // AttrsFunc is called when a span is started. The attributes it returns // are set on the Span being started. AttrsFunc func() []attribute.KeyValue } func (a Annotator) OnStart(_ context.Context, s ReadWriteSpan) { s.SetAttributes(a.AttrsFunc()...) } func (a Annotator) Shutdown(context.Context) error { return nil } func (a Annotator) ForceFlush(context.Context) error { return nil } func (a Annotator) OnEnd(s ReadOnlySpan) { attr := s.Attributes()[0] fmt.Printf("%s: %s\n", attr.Key, attr.Value.AsString()) } func ExampleSpanProcessor_annotated() { a := Annotator{ AttrsFunc: func() []attribute.KeyValue { return []attribute.KeyValue{ownerKey.String(owner)} }, } tracer := NewTracerProvider(WithSpanProcessor(a)).Tracer("annotated") // Simulate the situation where we want to annotate spans with an owner, // but at startup we do not now this information. Instead of waiting for // the owner to be known before starting and blocking here, start doing // work and update when the information becomes available. ctx := context.Background() _, s0 := tracer.Start(ctx, "span0") // Simulate an asynchronous call to determine the owner succeeding. We now // know that the owner of this application has been determined to be // Alice. Make sure all subsequent spans are annotated appropriately. owner = "alice" _, s1 := tracer.Start(ctx, "span1") s0.End() s1.End() // Output: // owner: unknown // owner: alice } golang-opentelemetry-otel-1.1.0/sdk/trace/span_processor_filter_example_test.go000066400000000000000000000061721414226744000302120ustar00rootroot00000000000000// 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 trace import ( "context" "time" ) // DurationFilter is a SpanProcessor that filters spans that have lifetimes // outside of a defined range. type DurationFilter struct { // Next is the next SpanProcessor in the chain. Next SpanProcessor // Min is the duration under which spans are dropped. Min time.Duration // Max is the duration over which spans are dropped. Max time.Duration } func (f DurationFilter) OnStart(parent context.Context, s ReadWriteSpan) { f.Next.OnStart(parent, s) } func (f DurationFilter) Shutdown(ctx context.Context) error { return f.Next.Shutdown(ctx) } func (f DurationFilter) ForceFlush(ctx context.Context) error { return f.Next.ForceFlush(ctx) } func (f DurationFilter) OnEnd(s ReadOnlySpan) { if f.Min > 0 && s.EndTime().Sub(s.StartTime()) < f.Min { // Drop short lived spans. return } if f.Max > 0 && s.EndTime().Sub(s.StartTime()) > f.Max { // Drop long lived spans. return } f.Next.OnEnd(s) } // InstrumentationBlacklist is a SpanProcessor that drops all spans from // certain instrumentation. type InstrumentationBlacklist struct { // Next is the next SpanProcessor in the chain. Next SpanProcessor // Blacklist is the set of instrumentation names for which spans will be // dropped. Blacklist map[string]bool } func (f InstrumentationBlacklist) OnStart(parent context.Context, s ReadWriteSpan) { f.Next.OnStart(parent, s) } func (f InstrumentationBlacklist) Shutdown(ctx context.Context) error { return f.Next.Shutdown(ctx) } func (f InstrumentationBlacklist) ForceFlush(ctx context.Context) error { return f.Next.ForceFlush(ctx) } func (f InstrumentationBlacklist) OnEnd(s ReadOnlySpan) { if f.Blacklist != nil && f.Blacklist[s.InstrumentationLibrary().Name] { // Drop spans from this instrumentation return } f.Next.OnEnd(s) } type noopExporter struct{} func (noopExporter) ExportSpans(context.Context, []ReadOnlySpan) error { return nil } func (noopExporter) Shutdown(context.Context) error { return nil } func ExampleSpanProcessor_filtered() { exportSP := NewSimpleSpanProcessor(noopExporter{}) // Build a SpanProcessor chain to filter out all spans from the pernicious // "naughty-instrumentation" dependency and only allow spans shorter than // an minute and longer than a second to be exported with the exportSP. filter := DurationFilter{ Next: InstrumentationBlacklist{ Next: exportSP, Blacklist: map[string]bool{ "naughty-instrumentation": true, }, }, Min: time.Second, Max: time.Minute, } _ = NewTracerProvider(WithSpanProcessor(filter)) // ... } golang-opentelemetry-otel-1.1.0/sdk/trace/span_processor_test.go000066400000000000000000000162101414226744000251240ustar00rootroot00000000000000// 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 trace_test import ( "context" "testing" "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" ) type testSpanProcessor struct { name string spansStarted []sdktrace.ReadWriteSpan spansEnded []sdktrace.ReadOnlySpan shutdownCount int } func (t *testSpanProcessor) OnStart(parent context.Context, s sdktrace.ReadWriteSpan) { psc := trace.SpanContextFromContext(parent) kv := []attribute.KeyValue{ { Key: "SpanProcessorName", Value: attribute.StringValue(t.name), }, // Store parent trace ID and span ID as attributes to be read later in // tests so that we "do something" with the parent argument. Real // SpanProcessor implementations will likely use the parent argument in // a more meaningful way. { Key: "ParentTraceID", Value: attribute.StringValue(psc.TraceID().String()), }, { Key: "ParentSpanID", Value: attribute.StringValue(psc.SpanID().String()), }, } s.AddEvent("OnStart", trace.WithAttributes(kv...)) t.spansStarted = append(t.spansStarted, s) } func (t *testSpanProcessor) OnEnd(s sdktrace.ReadOnlySpan) { t.spansEnded = append(t.spansEnded, s) } func (t *testSpanProcessor) Shutdown(_ context.Context) error { t.shutdownCount++ return nil } func (t *testSpanProcessor) ForceFlush(context.Context) error { return nil } func TestRegisterSpanProcessor(t *testing.T) { name := "Register span processor before span starts" tp := basicTracerProvider(t) spNames := []string{"sp1", "sp2", "sp3"} sps := NewNamedTestSpanProcessors(spNames) for _, sp := range sps { tp.RegisterSpanProcessor(sp) } tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ := trace.SpanIDFromHex("0102040810203040") parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) tr := tp.Tracer("SpanProcessor") _, span := tr.Start(ctx, "OnStart") span.End() wantCount := 1 for _, sp := range sps { gotCount := len(sp.spansStarted) if gotCount != wantCount { t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount) } gotCount = len(sp.spansEnded) if gotCount != wantCount { t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount) } c := 0 tidOK := false sidOK := false for _, e := range sp.spansStarted[0].Events() { for _, kv := range e.Attributes { switch kv.Key { case "SpanProcessorName": gotValue := kv.Value.AsString() if gotValue != spNames[c] { t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, spNames[c]) } c++ case "ParentTraceID": gotValue := kv.Value.AsString() if gotValue != parent.TraceID().String() { t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.TraceID()) } tidOK = true case "ParentSpanID": gotValue := kv.Value.AsString() if gotValue != parent.SpanID().String() { t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.SpanID()) } sidOK = true default: continue } } } if c != len(spNames) { t.Errorf("%s: expected attributes(SpanProcessorName): got %d, want %d\n", name, c, len(spNames)) } if !tidOK { t.Errorf("%s: expected attributes(ParentTraceID)\n", name) } if !sidOK { t.Errorf("%s: expected attributes(ParentSpanID)\n", name) } } } func TestUnregisterSpanProcessor(t *testing.T) { name := "Start span after unregistering span processor" tp := basicTracerProvider(t) spNames := []string{"sp1", "sp2", "sp3"} sps := NewNamedTestSpanProcessors(spNames) for _, sp := range sps { tp.RegisterSpanProcessor(sp) } tr := tp.Tracer("SpanProcessor") _, span := tr.Start(context.Background(), "OnStart") span.End() for _, sp := range sps { tp.UnregisterSpanProcessor(sp) } // start another span after unregistering span processor. _, span = tr.Start(context.Background(), "Start span after unregister") span.End() for _, sp := range sps { wantCount := 1 gotCount := len(sp.spansStarted) if gotCount != wantCount { t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount) } gotCount = len(sp.spansEnded) if gotCount != wantCount { t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount) } } } func TestUnregisterSpanProcessorWhileSpanIsActive(t *testing.T) { name := "Unregister span processor while span is active" tp := basicTracerProvider(t) sp := NewTestSpanProcessor("sp") tp.RegisterSpanProcessor(sp) tr := tp.Tracer("SpanProcessor") _, span := tr.Start(context.Background(), "OnStart") tp.UnregisterSpanProcessor(sp) span.End() wantCount := 1 gotCount := len(sp.spansStarted) if gotCount != wantCount { t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount) } wantCount = 0 gotCount = len(sp.spansEnded) if gotCount != wantCount { t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount) } } func TestSpanProcessorShutdown(t *testing.T) { name := "Increment shutdown counter of a span processor" tp := basicTracerProvider(t) sp := NewTestSpanProcessor("sp") if sp == nil { t.Fatalf("Error creating new instance of TestSpanProcessor\n") } tp.RegisterSpanProcessor(sp) wantCount := 1 err := sp.Shutdown(context.Background()) if err != nil { t.Error("Error shutting the testSpanProcessor down\n") } gotCount := sp.shutdownCount if wantCount != gotCount { t.Errorf("%s: wrong counter: got %d, want %d\n", name, gotCount, wantCount) } } func TestMultipleUnregisterSpanProcessorCalls(t *testing.T) { name := "Increment shutdown counter after first UnregisterSpanProcessor call" tp := basicTracerProvider(t) sp := NewTestSpanProcessor("sp") if sp == nil { t.Fatalf("Error creating new instance of TestSpanProcessor\n") } wantCount := 1 tp.RegisterSpanProcessor(sp) tp.UnregisterSpanProcessor(sp) gotCount := sp.shutdownCount if wantCount != gotCount { t.Errorf("%s: wrong counter: got %d, want %d\n", name, gotCount, wantCount) } // Multiple UnregisterSpanProcessor should not trigger multiple Shutdown calls. tp.UnregisterSpanProcessor(sp) gotCount = sp.shutdownCount if wantCount != gotCount { t.Errorf("%s: wrong counter: got %d, want %d\n", name, gotCount, wantCount) } } func NewTestSpanProcessor(name string) *testSpanProcessor { return &testSpanProcessor{name: name} } func NewNamedTestSpanProcessors(names []string) []*testSpanProcessor { tsp := []*testSpanProcessor{} for _, n := range names { tsp = append(tsp, NewTestSpanProcessor(n)) } return tsp } golang-opentelemetry-otel-1.1.0/sdk/trace/trace_test.go000066400000000000000000001570411414226744000231720ustar00rootroot00000000000000// 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 trace import ( "context" "errors" "fmt" "math" "strconv" "strings" "sync" "sync/atomic" "testing" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" "go.opentelemetry.io/otel/trace" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ottest "go.opentelemetry.io/otel/internal/internaltest" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" ) const envVar = "OTEL_RESOURCE_ATTRIBUTES" type storingHandler struct { errs []error } func (s *storingHandler) Handle(err error) { s.errs = append(s.errs, err) } func (s *storingHandler) Reset() { s.errs = nil } var ( tid trace.TraceID sid trace.SpanID sc trace.SpanContext handler = &storingHandler{} ) func init() { tid, _ = trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ = trace.SpanIDFromHex("0102040810203040") sc = trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, }) otel.SetErrorHandler(handler) } func TestTracerFollowsExpectedAPIBehaviour(t *testing.T) { harness := ottest.NewHarness(t) harness.TestTracerProvider(func() trace.TracerProvider { return NewTracerProvider(WithSampler(TraceIDRatioBased(0))) }) tp := NewTracerProvider(WithSampler(TraceIDRatioBased(0))) harness.TestTracer(func() trace.Tracer { return tp.Tracer("") }) } type testExporter struct { mu sync.RWMutex idx map[string]int spans []*snapshot } func NewTestExporter() *testExporter { return &testExporter{idx: make(map[string]int)} } func (te *testExporter) ExportSpans(_ context.Context, spans []ReadOnlySpan) error { te.mu.Lock() defer te.mu.Unlock() i := len(te.spans) for _, s := range spans { te.idx[s.Name()] = i te.spans = append(te.spans, s.(*snapshot)) i++ } return nil } func (te *testExporter) Spans() []*snapshot { te.mu.RLock() defer te.mu.RUnlock() cp := make([]*snapshot, len(te.spans)) copy(cp, te.spans) return cp } func (te *testExporter) GetSpan(name string) (*snapshot, bool) { te.mu.RLock() defer te.mu.RUnlock() i, ok := te.idx[name] if !ok { return nil, false } return te.spans[i], true } func (te *testExporter) Len() int { te.mu.RLock() defer te.mu.RUnlock() return len(te.spans) } func (te *testExporter) Shutdown(context.Context) error { te.Reset() return nil } func (te *testExporter) Reset() { te.mu.Lock() defer te.mu.Unlock() te.idx = make(map[string]int) te.spans = te.spans[:0] } type testSampler struct { callCount int prefix string t *testing.T } func (ts *testSampler) ShouldSample(p SamplingParameters) SamplingResult { ts.callCount++ ts.t.Logf("called sampler for name %q", p.Name) decision := Drop if strings.HasPrefix(p.Name, ts.prefix) { decision = RecordAndSample } return SamplingResult{Decision: decision, Attributes: []attribute.KeyValue{attribute.Int("callCount", ts.callCount)}} } func (ts testSampler) Description() string { return "testSampler" } func TestSetName(t *testing.T) { tp := NewTracerProvider() type testCase struct { name string newName string } for idx, tt := range []testCase{ { // 0 name: "foobar", newName: "foobaz", }, { // 1 name: "foobar", newName: "barbaz", }, { // 2 name: "barbar", newName: "barbaz", }, { // 3 name: "barbar", newName: "foobar", }, } { sp := startNamedSpan(tp, "SetName", tt.name) if sdkspan, ok := sp.(*recordingSpan); ok { if sdkspan.Name() != tt.name { t.Errorf("%d: invalid name at span creation, expected %v, got %v", idx, tt.name, sdkspan.Name()) } } else { t.Errorf("%d: unable to coerce span to SDK span, is type %T", idx, sp) } sp.SetName(tt.newName) if sdkspan, ok := sp.(*recordingSpan); ok { if sdkspan.Name() != tt.newName { t.Errorf("%d: span name not changed, expected %v, got %v", idx, tt.newName, sdkspan.Name()) } } else { t.Errorf("%d: unable to coerce span to SDK span, is type %T", idx, sp) } sp.End() } } func TestSpanIsRecording(t *testing.T) { t.Run("while Span active", func(t *testing.T) { for name, tc := range map[string]struct { sampler Sampler want bool }{ "Always sample, recording on": {sampler: AlwaysSample(), want: true}, "Never sample recording off": {sampler: NeverSample(), want: false}, } { tp := NewTracerProvider(WithSampler(tc.sampler)) _, span := tp.Tracer(name).Start(context.Background(), "StartSpan") defer span.End() got := span.IsRecording() assert.Equal(t, got, tc.want, name) } }) t.Run("after Span end", func(t *testing.T) { for name, tc := range map[string]Sampler{ "Always Sample": AlwaysSample(), "Never Sample": NeverSample(), } { tp := NewTracerProvider(WithSampler(tc)) _, span := tp.Tracer(name).Start(context.Background(), "StartSpan") span.End() got := span.IsRecording() assert.False(t, got, name) } }) } func TestSampling(t *testing.T) { idg := defaultIDGenerator() const total = 10000 for name, tc := range map[string]struct { sampler Sampler expect float64 parent bool sampledParent bool }{ // Span w/o a parent "NeverSample": {sampler: NeverSample(), expect: 0}, "AlwaysSample": {sampler: AlwaysSample(), expect: 1.0}, "TraceIdRatioBased_-1": {sampler: TraceIDRatioBased(-1.0), expect: 0}, "TraceIdRatioBased_.25": {sampler: TraceIDRatioBased(0.25), expect: .25}, "TraceIdRatioBased_.50": {sampler: TraceIDRatioBased(0.50), expect: .5}, "TraceIdRatioBased_.75": {sampler: TraceIDRatioBased(0.75), expect: .75}, "TraceIdRatioBased_2.0": {sampler: TraceIDRatioBased(2.0), expect: 1}, // Spans w/o a parent and using ParentBased(DelegateSampler()) Sampler, receive DelegateSampler's sampling decision "ParentNeverSample": {sampler: ParentBased(NeverSample()), expect: 0}, "ParentAlwaysSample": {sampler: ParentBased(AlwaysSample()), expect: 1}, "ParentTraceIdRatioBased_.50": {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: .5}, // An unadorned TraceIDRatioBased sampler ignores parent spans "UnsampledParentSpanWithTraceIdRatioBased_.25": {sampler: TraceIDRatioBased(0.25), expect: .25, parent: true}, "SampledParentSpanWithTraceIdRatioBased_.25": {sampler: TraceIDRatioBased(0.25), expect: .25, parent: true, sampledParent: true}, "UnsampledParentSpanWithTraceIdRatioBased_.50": {sampler: TraceIDRatioBased(0.50), expect: .5, parent: true}, "SampledParentSpanWithTraceIdRatioBased_.50": {sampler: TraceIDRatioBased(0.50), expect: .5, parent: true, sampledParent: true}, "UnsampledParentSpanWithTraceIdRatioBased_.75": {sampler: TraceIDRatioBased(0.75), expect: .75, parent: true}, "SampledParentSpanWithTraceIdRatioBased_.75": {sampler: TraceIDRatioBased(0.75), expect: .75, parent: true, sampledParent: true}, // Spans with a sampled parent but using NeverSample Sampler, are not sampled "SampledParentSpanWithNeverSample": {sampler: NeverSample(), expect: 0, parent: true, sampledParent: true}, // Spans with a sampled parent and using ParentBased(DelegateSampler()) Sampler, inherit the parent span's sampling status "SampledParentSpanWithParentNeverSample": {sampler: ParentBased(NeverSample()), expect: 1, parent: true, sampledParent: true}, "UnsampledParentSpanWithParentNeverSampler": {sampler: ParentBased(NeverSample()), expect: 0, parent: true, sampledParent: false}, "SampledParentSpanWithParentAlwaysSampler": {sampler: ParentBased(AlwaysSample()), expect: 1, parent: true, sampledParent: true}, "UnsampledParentSpanWithParentAlwaysSampler": {sampler: ParentBased(AlwaysSample()), expect: 0, parent: true, sampledParent: false}, "SampledParentSpanWithParentTraceIdRatioBased_.50": {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: 1, parent: true, sampledParent: true}, "UnsampledParentSpanWithParentTraceIdRatioBased_.50": {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: 0, parent: true, sampledParent: false}, } { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() p := NewTracerProvider(WithSampler(tc.sampler)) tr := p.Tracer("test") var sampled int for i := 0; i < total; i++ { ctx := context.Background() if tc.parent { tid, sid := idg.NewIDs(ctx) psc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, }) if tc.sampledParent { psc = psc.WithTraceFlags(trace.FlagsSampled) } ctx = trace.ContextWithRemoteSpanContext(ctx, psc) } _, span := tr.Start(ctx, "test") if span.SpanContext().IsSampled() { sampled++ } } tolerance := 0.0 got := float64(sampled) / float64(total) if tc.expect > 0 && tc.expect < 1 { // See https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval const z = 4.75342 // This should succeed 99.9999% of the time tolerance = z * math.Sqrt(got*(1-got)/total) } diff := math.Abs(got - tc.expect) if diff > tolerance { t.Errorf("got %f (diff: %f), expected %f (w/tolerance: %f)", got, diff, tc.expect, tolerance) } }) } } func TestStartSpanWithParent(t *testing.T) { tp := NewTracerProvider() tr := tp.Tracer("SpanWithParent") ctx := context.Background() _, s1 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span1-unsampled-parent1") if err := checkChild(t, sc, s1); err != nil { t.Error(err) } _, s2 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span2-unsampled-parent1") if err := checkChild(t, sc, s2); err != nil { t.Error(err) } ts, err := trace.ParseTraceState("k=v") if err != nil { t.Error(err) } sc2 := sc.WithTraceState(ts) _, s3 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span3-sampled-parent2") if err := checkChild(t, sc2, s3); err != nil { t.Error(err) } ctx2, s4 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span4-sampled-parent2") if err := checkChild(t, sc2, s4); err != nil { t.Error(err) } s4Sc := s4.SpanContext() _, s5 := tr.Start(ctx2, "span5-implicit-childof-span4") if err := checkChild(t, s4Sc, s5); err != nil { t.Error(err) } } func TestStartSpanNewRootNotSampled(t *testing.T) { alwaysSampleTp := NewTracerProvider() sampledTr := alwaysSampleTp.Tracer("AlwaysSampled") neverSampleTp := NewTracerProvider(WithSampler(ParentBased(NeverSample()))) neverSampledTr := neverSampleTp.Tracer("ParentBasedNeverSample") ctx := context.Background() ctx, s1 := sampledTr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span1-sampled") if err := checkChild(t, sc, s1); err != nil { t.Error(err) } _, s2 := neverSampledTr.Start(ctx, "span2-no-newroot") if !s2.SpanContext().IsSampled() { t.Error(fmt.Errorf("got child span is not sampled, want child span with sampler: ParentBased(NeverSample()) to be sampled")) } // Adding WithNewRoot causes child spans to not sample based on parent context _, s3 := neverSampledTr.Start(ctx, "span3-newroot", trace.WithNewRoot()) if s3.SpanContext().IsSampled() { t.Error(fmt.Errorf("got child span is sampled, want child span WithNewRoot() and with sampler: ParentBased(NeverSample()) to not be sampled")) } } func TestSetSpanAttributesOnStart(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "StartSpanAttribute", trace.WithAttributes(attribute.String("key1", "value1")), trace.WithAttributes(attribute.String("key2", "value2")), ) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: []attribute.KeyValue{ attribute.String("key1", "value1"), attribute.String("key2", "value2"), }, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "StartSpanAttribute"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanAttributesOnStart: -got +want %s", diff) } } func TestSetSpanAttributes(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "SpanAttribute") span.SetAttributes(attribute.String("key1", "value1")) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: []attribute.KeyValue{ attribute.String("key1", "value1"), }, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "SpanAttribute"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanAttributes: -got +want %s", diff) } } func TestSamplerAttributesLocalChildSpan(t *testing.T) { sampler := &testSampler{prefix: "span", t: t} te := NewTestExporter() tp := NewTracerProvider(WithSampler(sampler), WithSyncer(te), WithResource(resource.Empty())) ctx := context.Background() ctx, span := startLocalSpan(tp, ctx, "SpanOne", "span0") _, spanTwo := startLocalSpan(tp, ctx, "SpanTwo", "span1") spanTwo.End() span.End() got := te.Spans() require.Len(t, got, 2) // FILO order above means spanTwo <-> gotSpan0 and span <-> gotSpan1. gotSpan0, gotSpan1 := got[0], got[1] // Ensure sampler is called for local child spans by verifying the // attributes set by the sampler are set on the child span. assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 2)}, gotSpan0.Attributes()) assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 1)}, gotSpan1.Attributes()) } func TestSetSpanAttributesOverLimit(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSpanLimits(SpanLimits{AttributeCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "SpanAttributesOverLimit") span.SetAttributes( attribute.Bool("key1", true), attribute.String("key2", "value2"), attribute.Bool("key1", false), // Replace key1. attribute.Int64("key4", 4), // Remove key2 and add key4 ) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: []attribute.KeyValue{ attribute.Bool("key1", false), attribute.Int64("key4", 4), }, spanKind: trace.SpanKindInternal, droppedAttributeCount: 1, instrumentationLibrary: instrumentation.Library{Name: "SpanAttributesOverLimit"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanAttributesOverLimit: -got +want %s", diff) } } func TestSetSpanAttributesWithInvalidKey(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSpanLimits(SpanLimits{}), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "SpanToSetInvalidKeyOrValue") span.SetAttributes( attribute.Bool("", true), attribute.Bool("key1", false), ) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: []attribute.KeyValue{ attribute.Bool("key1", false), }, spanKind: trace.SpanKindInternal, droppedAttributeCount: 0, instrumentationLibrary: instrumentation.Library{Name: "SpanToSetInvalidKeyOrValue"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanAttributesWithInvalidKey: -got +want %s", diff) } } func TestEvents(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "Events") k1v1 := attribute.String("key1", "value1") k2v2 := attribute.Bool("key2", true) k3v3 := attribute.Int64("key3", 3) span.AddEvent("foo", trace.WithAttributes(attribute.String("key1", "value1"))) span.AddEvent("bar", trace.WithAttributes( attribute.Bool("key2", true), attribute.Int64("key3", 3), )) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } for i := range got.Events() { if !checkTime(&got.Events()[i].Time) { t.Error("exporting span: expected nonzero Event Time") } } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", events: []Event{ {Name: "foo", Attributes: []attribute.KeyValue{k1v1}}, {Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}}, }, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "Events"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("Message Events: -got +want %s", diff) } } func TestEventsOverLimit(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSpanLimits(SpanLimits{EventCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "EventsOverLimit") k1v1 := attribute.String("key1", "value1") k2v2 := attribute.Bool("key2", false) k3v3 := attribute.String("key3", "value3") span.AddEvent("fooDrop", trace.WithAttributes(attribute.String("key1", "value1"))) span.AddEvent("barDrop", trace.WithAttributes( attribute.Bool("key2", true), attribute.String("key3", "value3"), )) span.AddEvent("foo", trace.WithAttributes(attribute.String("key1", "value1"))) span.AddEvent("bar", trace.WithAttributes( attribute.Bool("key2", false), attribute.String("key3", "value3"), )) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } for i := range got.Events() { if !checkTime(&got.Events()[i].Time) { t.Error("exporting span: expected nonzero Event Time") } } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", events: []Event{ {Name: "foo", Attributes: []attribute.KeyValue{k1v1}}, {Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}}, }, droppedEventCount: 2, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "EventsOverLimit"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("Message Event over limit: -got +want %s", diff) } } func TestLinks(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) k1v1 := attribute.String("key1", "value1") k2v2 := attribute.String("key2", "value2") k3v3 := attribute.String("key3", "value3") sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) l1 := trace.Link{SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1}} l2 := trace.Link{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2, k3v3}} links := []trace.Link{l1, l2} span := startSpan(tp, "Links", trace.WithLinks(links...)) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", links: []Link{{l1.SpanContext, l1.Attributes, 0}, {l2.SpanContext, l2.Attributes, 0}}, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "Links"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("Link: -got +want %s", diff) } sc1 = trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) span1 := startSpan(tp, "name", trace.WithLinks([]trace.Link{ {SpanContext: trace.SpanContext{}}, {SpanContext: sc1}, }...)) sdkspan, _ := span1.(*recordingSpan) require.Len(t, sdkspan.Links(), 1) } func TestLinksOverLimit(t *testing.T) { te := NewTestExporter() sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) sc3 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) tp := NewTracerProvider(WithSpanLimits(SpanLimits{LinkCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "LinksOverLimit", trace.WithLinks( trace.Link{SpanContext: sc1, Attributes: []attribute.KeyValue{attribute.String("key1", "value1")}}, trace.Link{SpanContext: sc2, Attributes: []attribute.KeyValue{attribute.String("key2", "value2")}}, trace.Link{SpanContext: sc3, Attributes: []attribute.KeyValue{attribute.String("key3", "value3")}}, ), ) k2v2 := attribute.String("key2", "value2") k3v3 := attribute.String("key3", "value3") got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", links: []Link{ {SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2}, DroppedAttributeCount: 0}, {SpanContext: sc3, Attributes: []attribute.KeyValue{k3v3}, DroppedAttributeCount: 0}, }, droppedLinkCount: 1, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "LinksOverLimit"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("Link over limit: -got +want %s", diff) } } func TestSetSpanName(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) ctx := context.Background() want := "SpanName-1" ctx = trace.ContextWithRemoteSpanContext(ctx, sc) _, span := tp.Tracer("SetSpanName").Start(ctx, "SpanName-1") got, err := endSpan(te, span) if err != nil { t.Fatal(err) } if got.Name() != want { t.Errorf("span.Name: got %q; want %q", got.Name(), want) } } func TestSetSpanStatus(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "SpanStatus") span.SetStatus(codes.Error, "Error") got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", spanKind: trace.SpanKindInternal, status: Status{ Code: codes.Error, Description: "Error", }, instrumentationLibrary: instrumentation.Library{Name: "SpanStatus"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanStatus: -got +want %s", diff) } } func TestSetSpanStatusWithoutMessageWhenStatusIsNotError(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "SpanStatus") span.SetStatus(codes.Ok, "This message will be ignored") got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", spanKind: trace.SpanKindInternal, status: Status{ Code: codes.Ok, Description: "", }, instrumentationLibrary: instrumentation.Library{Name: "SpanStatus"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanStatus: -got +want %s", diff) } } func cmpDiff(x, y interface{}) string { return cmp.Diff(x, y, cmp.AllowUnexported(snapshot{}), cmp.AllowUnexported(attribute.Value{}), cmp.AllowUnexported(Event{}), cmp.AllowUnexported(trace.TraceState{})) } // checkChild is test utility function that tests that c has fields set appropriately, // given that it is a child span of p. func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error { s := apiSpan.(*recordingSpan) if s == nil { return fmt.Errorf("got nil child span, want non-nil") } if got, want := s.spanContext.TraceID().String(), p.TraceID().String(); got != want { return fmt.Errorf("got child trace ID %s, want %s", got, want) } if childID, parentID := s.spanContext.SpanID().String(), p.SpanID().String(); childID == parentID { return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID) } if got, want := s.spanContext.TraceFlags(), p.TraceFlags(); got != want { return fmt.Errorf("got child trace options %d, want %d", got, want) } got, want := s.spanContext.TraceState(), p.TraceState() assert.Equal(t, want, got) return nil } // startSpan starts a span with a name "span0". See startNamedSpan for // details. func startSpan(tp *TracerProvider, trName string, args ...trace.SpanStartOption) trace.Span { return startNamedSpan(tp, trName, "span0", args...) } // startNamed Span is a test utility func that starts a span with a // passed name and with remote span context as parent. The remote span // context contains TraceFlags with sampled bit set. This allows the // span to be automatically sampled. func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanStartOption) trace.Span { _, span := tp.Tracer(trName).Start( trace.ContextWithRemoteSpanContext(context.Background(), sc), name, args..., ) return span } // startLocalSpan is a test utility func that starts a span with a // passed name and with the passed context. The context is returned // along with the span so this parent can be used to create child // spans. func startLocalSpan(tp *TracerProvider, ctx context.Context, trName, name string, args ...trace.SpanStartOption) (context.Context, trace.Span) { ctx, span := tp.Tracer(trName).Start( ctx, name, args..., ) return ctx, span } // endSpan is a test utility function that ends the span in the context and // returns the exported span. // It requires that span be sampled using one of these methods // 1. Passing parent span context in context // 2. Use WithSampler(AlwaysSample()) // 3. Configuring AlwaysSample() as default sampler // // It also does some basic tests on the span. // It also clears spanID in the to make the comparison easier. func endSpan(te *testExporter, span trace.Span) (*snapshot, error) { if !span.IsRecording() { return nil, fmt.Errorf("IsRecording: got false, want true") } if !span.SpanContext().IsSampled() { return nil, fmt.Errorf("IsSampled: got false, want true") } span.End() if te.Len() != 1 { return nil, fmt.Errorf("got %d exported spans, want one span", te.Len()) } got := te.Spans()[0] if !got.SpanContext().SpanID().IsValid() { return nil, fmt.Errorf("exporting span: expected nonzero SpanID") } got.spanContext = got.SpanContext().WithSpanID(trace.SpanID{}) if !checkTime(&got.startTime) { return nil, fmt.Errorf("exporting span: expected nonzero StartTime") } if !checkTime(&got.endTime) { return nil, fmt.Errorf("exporting span: expected nonzero EndTime") } return got, nil } // checkTime checks that a nonzero time was set in x, then clears it. func checkTime(x *time.Time) bool { if x.IsZero() { return false } *x = time.Time{} return true } func TestEndSpanTwice(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te)) st := time.Now() et1 := st.Add(100 * time.Millisecond) et2 := st.Add(200 * time.Millisecond) span := startSpan(tp, "EndSpanTwice", trace.WithTimestamp(st)) span.End(trace.WithTimestamp(et1)) span.End(trace.WithTimestamp(et2)) if te.Len() != 1 { t.Fatalf("expected only a single span, got %#v", te.Spans()) } ro := span.(ReadOnlySpan) if ro.EndTime() != et1 { t.Fatalf("2nd call to End() should not modify end time") } } func TestStartSpanAfterEnd(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSampler(AlwaysSample()), WithSyncer(te)) ctx := context.Background() tr := tp.Tracer("SpanAfterEnd") ctx, span0 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "parent") ctx1, span1 := tr.Start(ctx, "span-1") span1.End() // Start a new span with the context containing span-1 // even though span-1 is ended, we still add this as a new child of span-1 _, span2 := tr.Start(ctx1, "span-2") span2.End() span0.End() if got, want := te.Len(), 3; got != want { t.Fatalf("len(%#v) = %d; want %d", te.Spans(), got, want) } gotParent, ok := te.GetSpan("parent") if !ok { t.Fatal("parent not recorded") } gotSpan1, ok := te.GetSpan("span-1") if !ok { t.Fatal("span-1 not recorded") } gotSpan2, ok := te.GetSpan("span-2") if !ok { t.Fatal("span-2 not recorded") } if got, want := gotSpan1.SpanContext().TraceID(), gotParent.SpanContext().TraceID(); got != want { t.Errorf("span-1.TraceID=%q; want %q", got, want) } if got, want := gotSpan2.SpanContext().TraceID(), gotParent.SpanContext().TraceID(); got != want { t.Errorf("span-2.TraceID=%q; want %q", got, want) } if got, want := gotSpan1.Parent().SpanID(), gotParent.SpanContext().SpanID(); got != want { t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want) } if got, want := gotSpan2.Parent().SpanID(), gotSpan1.SpanContext().SpanID(); got != want { t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want) } } func TestChildSpanCount(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSampler(AlwaysSample()), WithSyncer(te)) tr := tp.Tracer("ChidSpanCount") ctx, span0 := tr.Start(context.Background(), "parent") ctx1, span1 := tr.Start(ctx, "span-1") _, span2 := tr.Start(ctx1, "span-2") span2.End() span1.End() _, span3 := tr.Start(ctx, "span-3") span3.End() span0.End() if got, want := te.Len(), 4; got != want { t.Fatalf("len(%#v) = %d; want %d", te.Spans(), got, want) } gotParent, ok := te.GetSpan("parent") if !ok { t.Fatal("parent not recorded") } gotSpan1, ok := te.GetSpan("span-1") if !ok { t.Fatal("span-1 not recorded") } gotSpan2, ok := te.GetSpan("span-2") if !ok { t.Fatal("span-2 not recorded") } gotSpan3, ok := te.GetSpan("span-3") if !ok { t.Fatal("span-3 not recorded") } if got, want := gotSpan3.ChildSpanCount(), 0; got != want { t.Errorf("span-3.ChildSpanCount=%d; want %d", got, want) } if got, want := gotSpan2.ChildSpanCount(), 0; got != want { t.Errorf("span-2.ChildSpanCount=%d; want %d", got, want) } if got, want := gotSpan1.ChildSpanCount(), 1; got != want { t.Errorf("span-1.ChildSpanCount=%d; want %d", got, want) } if got, want := gotParent.ChildSpanCount(), 2; got != want { t.Errorf("parent.ChildSpanCount=%d; want %d", got, want) } } func TestNilSpanEnd(t *testing.T) { var span *recordingSpan span.End() } func TestNonRecordingSpanDoesNotTrackRuntimeTracerTask(t *testing.T) { tp := NewTracerProvider(WithSampler(NeverSample())) tr := tp.Tracer("TestNonRecordingSpanDoesNotTrackRuntimeTracerTask") _, apiSpan := tr.Start(context.Background(), "foo") if _, ok := apiSpan.(runtimeTracer); ok { t.Fatalf("non recording span implements runtime trace task tracking") } } func TestRecordingSpanRuntimeTracerTaskEnd(t *testing.T) { tp := NewTracerProvider(WithSampler(AlwaysSample())) tr := tp.Tracer("TestRecordingSpanRuntimeTracerTaskEnd") var n uint64 executionTracerTaskEnd := func() { atomic.AddUint64(&n, 1) } _, apiSpan := tr.Start(context.Background(), "foo") s, ok := apiSpan.(*recordingSpan) if !ok { t.Fatal("recording span not returned from always sampled Tracer") } s.executionTracerTaskEnd = executionTracerTaskEnd s.End() if n != 1 { t.Error("recording span did not end runtime trace task") } } func TestCustomStartEndTime(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithSampler(AlwaysSample())) startTime := time.Date(2019, time.August, 27, 14, 42, 0, 0, time.UTC) endTime := startTime.Add(time.Second * 20) _, span := tp.Tracer("Custom Start and End time").Start( context.Background(), "testspan", trace.WithTimestamp(startTime), ) span.End(trace.WithTimestamp(endTime)) if te.Len() != 1 { t.Fatalf("got %d exported spans, want one span", te.Len()) } got := te.Spans()[0] if got.StartTime() != startTime { t.Errorf("expected start time to be %s, got %s", startTime, got.StartTime()) } if got.EndTime() != endTime { t.Errorf("expected end time to be %s, got %s", endTime, got.EndTime()) } } func TestRecordError(t *testing.T) { scenarios := []struct { err error typ string msg string }{ { err: ottest.NewTestError("test error"), typ: "go.opentelemetry.io/otel/internal/internaltest.TestError", msg: "test error", }, { err: errors.New("test error 2"), typ: "*errors.errorString", msg: "test error 2", }, } for _, s := range scenarios { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "RecordError") errTime := time.Now() span.RecordError(s.err, trace.WithTimestamp(errTime)) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", status: Status{Code: codes.Unset}, spanKind: trace.SpanKindInternal, events: []Event{ { Name: semconv.ExceptionEventName, Time: errTime, Attributes: []attribute.KeyValue{ semconv.ExceptionTypeKey.String(s.typ), semconv.ExceptionMessageKey.String(s.msg), }, }, }, instrumentationLibrary: instrumentation.Library{Name: "RecordError"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SpanErrorOptions: -got +want %s", diff) } } } func TestRecordErrorWithStackTrace(t *testing.T) { err := ottest.NewTestError("test error") typ := "go.opentelemetry.io/otel/internal/internaltest.TestError" msg := "test error" te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "RecordError") errTime := time.Now() span.RecordError(err, trace.WithTimestamp(errTime), trace.WithStackTrace(true)) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", status: Status{Code: codes.Unset}, spanKind: trace.SpanKindInternal, events: []Event{ { Name: semconv.ExceptionEventName, Time: errTime, Attributes: []attribute.KeyValue{ semconv.ExceptionTypeKey.String(typ), semconv.ExceptionMessageKey.String(msg), }, }, }, instrumentationLibrary: instrumentation.Library{Name: "RecordError"}, } assert.Equal(t, got.spanContext, want.spanContext) assert.Equal(t, got.parent, want.parent) assert.Equal(t, got.name, want.name) assert.Equal(t, got.status, want.status) assert.Equal(t, got.spanKind, want.spanKind) assert.Equal(t, got.events[0].Attributes[0].Value.AsString(), want.events[0].Attributes[0].Value.AsString()) assert.Equal(t, got.events[0].Attributes[1].Value.AsString(), want.events[0].Attributes[1].Value.AsString()) gotStackTraceFunctionName := strings.Split(got.events[0].Attributes[2].Value.AsString(), "\n") assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1]) assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError", gotStackTraceFunctionName[3]) } func TestRecordErrorNil(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "RecordErrorNil") span.RecordError(nil) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", spanKind: trace.SpanKindInternal, status: Status{ Code: codes.Unset, Description: "", }, instrumentationLibrary: instrumentation.Library{Name: "RecordErrorNil"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SpanErrorOptions: -got +want %s", diff) } } func TestWithSpanKind(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithSampler(AlwaysSample()), WithResource(resource.Empty())) tr := tp.Tracer("withSpanKind") _, span := tr.Start(context.Background(), "WithoutSpanKind") spanData, err := endSpan(te, span) if err != nil { t.Error(err.Error()) } if spanData.SpanKind() != trace.SpanKindInternal { t.Errorf("Default value of Spankind should be Internal: got %+v, want %+v\n", spanData.SpanKind(), trace.SpanKindInternal) } sks := []trace.SpanKind{ trace.SpanKindInternal, trace.SpanKindServer, trace.SpanKindClient, trace.SpanKindProducer, trace.SpanKindConsumer, } for _, sk := range sks { te.Reset() _, span := tr.Start(context.Background(), fmt.Sprintf("SpanKind-%v", sk), trace.WithSpanKind(sk)) spanData, err := endSpan(te, span) if err != nil { t.Error(err.Error()) } if spanData.SpanKind() != sk { t.Errorf("WithSpanKind check: got %+v, want %+v\n", spanData.SpanKind(), sks) } } } func mergeResource(t *testing.T, r1, r2 *resource.Resource) *resource.Resource { r, err := resource.Merge(r1, r2) assert.NoError(t, err) return r } func TestWithResource(t *testing.T) { store, err := ottest.SetEnvVariables(map[string]string{ envVar: "key=value,rk5=7", }) require.NoError(t, err) defer func() { require.NoError(t, store.Restore()) }() cases := []struct { name string options []TracerProviderOption want *resource.Resource msg string }{ { name: "explicitly empty resource", options: []TracerProviderOption{WithResource(resource.Empty())}, want: resource.Environment(), }, { name: "uses default if no resource option", options: []TracerProviderOption{}, want: resource.Default(), }, { name: "explicit resource", options: []TracerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)))}, want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))), }, { name: "last resource wins", options: []TracerProviderOption{ WithResource(resource.NewSchemaless(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))), WithResource(resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10)))}, want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))), }, { name: "overlapping attributes with environment resource", options: []TracerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)))}, want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))), }, } for _, tc := range cases { tc := tc t.Run(tc.name, func(t *testing.T) { te := NewTestExporter() defaultOptions := []TracerProviderOption{WithSyncer(te), WithSampler(AlwaysSample())} tp := NewTracerProvider(append(defaultOptions, tc.options...)...) span := startSpan(tp, "WithResource") span.SetAttributes(attribute.String("key1", "value1")) got, err := endSpan(te, span) if err != nil { t.Error(err.Error()) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: []attribute.KeyValue{ attribute.String("key1", "value1"), }, spanKind: trace.SpanKindInternal, resource: tc.want, instrumentationLibrary: instrumentation.Library{Name: "WithResource"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("WithResource:\n -got +want %s", diff) } }) } } func TestWithInstrumentationVersionAndSchema(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) ctx := context.Background() ctx = trace.ContextWithRemoteSpanContext(ctx, sc) _, span := tp.Tracer( "WithInstrumentationVersion", trace.WithInstrumentationVersion("v0.1.0"), trace.WithSchemaURL("https://opentelemetry.io/schemas/1.2.0"), ).Start(ctx, "span0") got, err := endSpan(te, span) if err != nil { t.Error(err.Error()) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{ Name: "WithInstrumentationVersion", Version: "v0.1.0", SchemaURL: "https://opentelemetry.io/schemas/1.2.0", }, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("WithResource:\n -got +want %s", diff) } } func TestSpanCapturesPanic(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) _, span := tp.Tracer("CatchPanic").Start( context.Background(), "span", ) f := func() { defer span.End() panic(errors.New("error message")) } require.PanicsWithError(t, "error message", f) spans := te.Spans() require.Len(t, spans, 1) require.Len(t, spans[0].Events(), 1) assert.Equal(t, spans[0].Events()[0].Name, semconv.ExceptionEventName) assert.Equal(t, spans[0].Events()[0].Attributes, []attribute.KeyValue{ semconv.ExceptionTypeKey.String("*errors.errorString"), semconv.ExceptionMessageKey.String("error message"), }) } func TestSpanCapturesPanicWithStackTrace(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty())) _, span := tp.Tracer("CatchPanic").Start( context.Background(), "span", ) f := func() { defer span.End(trace.WithStackTrace(true)) panic(errors.New("error message")) } require.PanicsWithError(t, "error message", f) spans := te.Spans() require.Len(t, spans, 1) require.Len(t, spans[0].Events(), 1) assert.Equal(t, spans[0].Events()[0].Name, semconv.ExceptionEventName) assert.Equal(t, spans[0].Events()[0].Attributes[0].Value.AsString(), "*errors.errorString") assert.Equal(t, spans[0].Events()[0].Attributes[1].Value.AsString(), "error message") gotStackTraceFunctionName := strings.Split(spans[0].Events()[0].Attributes[2].Value.AsString(), "\n") assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1]) assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End", gotStackTraceFunctionName[3]) } func TestReadOnlySpan(t *testing.T) { kv := attribute.String("foo", "bar") tp := NewTracerProvider(WithResource(resource.NewSchemaless(kv))) tr := tp.Tracer("ReadOnlySpan", trace.WithInstrumentationVersion("3")) // Initialize parent context. tID, sID := tp.idGenerator.NewIDs(context.Background()) parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, Remote: true, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) // Initialize linked context. tID, sID = tp.idGenerator.NewIDs(context.Background()) linked := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, }) st := time.Now() ctx, s := tr.Start(ctx, "foo", trace.WithTimestamp(st), trace.WithLinks(trace.Link{SpanContext: linked})) s.SetAttributes(kv) s.AddEvent("foo", trace.WithAttributes(kv)) s.SetStatus(codes.Ok, "foo") // Verify span implements ReadOnlySpan. ro, ok := s.(ReadOnlySpan) require.True(t, ok) assert.Equal(t, "foo", ro.Name()) assert.Equal(t, trace.SpanContextFromContext(ctx), ro.SpanContext()) assert.Equal(t, parent, ro.Parent()) assert.Equal(t, trace.SpanKindInternal, ro.SpanKind()) assert.Equal(t, st, ro.StartTime()) assert.True(t, ro.EndTime().IsZero()) assert.Equal(t, kv.Key, ro.Attributes()[0].Key) assert.Equal(t, kv.Value, ro.Attributes()[0].Value) assert.Equal(t, linked, ro.Links()[0].SpanContext) assert.Equal(t, kv.Key, ro.Events()[0].Attributes[0].Key) assert.Equal(t, kv.Value, ro.Events()[0].Attributes[0].Value) assert.Equal(t, codes.Ok, ro.Status().Code) assert.Equal(t, "", ro.Status().Description) assert.Equal(t, "ReadOnlySpan", ro.InstrumentationLibrary().Name) assert.Equal(t, "3", ro.InstrumentationLibrary().Version) assert.Equal(t, kv.Key, ro.Resource().Attributes()[0].Key) assert.Equal(t, kv.Value, ro.Resource().Attributes()[0].Value) // Verify changes to the original span are reflected in the ReadOnlySpan. s.SetName("bar") assert.Equal(t, "bar", ro.Name()) // Verify snapshot() returns snapshots that are independent from the // original span and from one another. d1 := s.(*recordingSpan).snapshot() s.AddEvent("baz") d2 := s.(*recordingSpan).snapshot() for _, e := range d1.Events() { if e.Name == "baz" { t.Errorf("Didn't expect to find 'baz' event") } } var exists bool for _, e := range d2.Events() { if e.Name == "baz" { exists = true } } if !exists { t.Errorf("Expected to find 'baz' event") } et := st.Add(time.Millisecond) s.End(trace.WithTimestamp(et)) assert.Equal(t, et, ro.EndTime()) } func TestReadWriteSpan(t *testing.T) { tp := NewTracerProvider(WithResource(resource.Empty())) tr := tp.Tracer("ReadWriteSpan") // Initialize parent context. tID, sID := tp.idGenerator.NewIDs(context.Background()) parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) _, span := tr.Start(ctx, "foo") defer span.End() // Verify span implements ReadOnlySpan. rw, ok := span.(ReadWriteSpan) require.True(t, ok) // Verify the span can be read from. assert.False(t, rw.StartTime().IsZero()) // Verify the span can be written to. rw.SetName("bar") assert.Equal(t, "bar", rw.Name()) // NOTE: This function tests ReadWriteSpan which is an interface which // embeds trace.Span and ReadOnlySpan. Since both of these interfaces have // their own tests, there is no point in testing all the possible methods // available via ReadWriteSpan as doing so would mean creating a lot of // duplication. } func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider( WithSpanLimits(SpanLimits{AttributePerEventCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty()), ) span := startSpan(tp, "AddSpanEventWithOverLimitedAttributes") span.AddEvent("test1", trace.WithAttributes( attribute.Bool("key1", true), attribute.String("key2", "value2"), )) // Parts of the attribute should be discard span.AddEvent("test2", trace.WithAttributes( attribute.Bool("key1", true), attribute.String("key2", "value2"), attribute.String("key3", "value3"), attribute.String("key4", "value4"), )) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } for i := range got.Events() { if !checkTime(&got.Events()[i].Time) { t.Error("exporting span: expected nonzero Event Time") } } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", attributes: nil, events: []Event{ { Name: "test1", Attributes: []attribute.KeyValue{ attribute.Bool("key1", true), attribute.String("key2", "value2"), }, }, { Name: "test2", Attributes: []attribute.KeyValue{ attribute.Bool("key1", true), attribute.String("key2", "value2"), }, DroppedAttributeCount: 2, }, }, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "AddSpanEventWithOverLimitedAttributes"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("SetSpanAttributesOverLimit: -got +want %s", diff) } } func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider( WithSpanLimits(SpanLimits{AttributePerLinkCountLimit: 1}), WithSyncer(te), WithResource(resource.Empty()), ) k1v1 := attribute.String("key1", "value1") k2v2 := attribute.String("key2", "value2") k3v3 := attribute.String("key3", "value3") k4v4 := attribute.String("key4", "value4") sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) span := startSpan(tp, "Links", trace.WithLinks([]trace.Link{ {SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1, k2v2}}, {SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2, k3v3, k4v4}}, }...)) got, err := endSpan(te, span) if err != nil { t.Fatal(err) } want := &snapshot{ spanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, }), parent: sc.WithRemote(true), name: "span0", links: []Link{ { SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1}, DroppedAttributeCount: 1, }, { SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2}, DroppedAttributeCount: 2, }, }, spanKind: trace.SpanKindInternal, instrumentationLibrary: instrumentation.Library{Name: "Links"}, } if diff := cmpDiff(got, want); diff != "" { t.Errorf("Link: -got +want %s", diff) } } type stateSampler struct { prefix string f func(trace.TraceState) trace.TraceState } func (s *stateSampler) ShouldSample(p SamplingParameters) SamplingResult { decision := Drop if strings.HasPrefix(p.Name, s.prefix) { decision = RecordAndSample } ts := s.f(trace.SpanContextFromContext(p.ParentContext).TraceState()) return SamplingResult{Decision: decision, Tracestate: ts} } func (s stateSampler) Description() string { return "stateSampler" } // Check that a new span propagates the SamplerResult.TraceState func TestSamplerTraceState(t *testing.T) { mustTS := func(ts trace.TraceState, err error) trace.TraceState { require.NoError(t, err) return ts } makeInserter := func(k, v, prefix string) Sampler { return &stateSampler{ prefix: prefix, f: func(t trace.TraceState) trace.TraceState { return mustTS(t.Insert(k, v)) }, } } makeDeleter := func(k, prefix string) Sampler { return &stateSampler{ prefix: prefix, f: func(t trace.TraceState) trace.TraceState { return t.Delete(k) }, } } clearer := func(prefix string) Sampler { return &stateSampler{ prefix: prefix, f: func(t trace.TraceState) trace.TraceState { return trace.TraceState{} }, } } tests := []struct { name string sampler Sampler spanName string input trace.TraceState want trace.TraceState exportSpan bool }{ { name: "alwaysOn", sampler: AlwaysSample(), input: mustTS(trace.ParseTraceState("k1=v1")), want: mustTS(trace.ParseTraceState("k1=v1")), exportSpan: true, }, { name: "alwaysOff", sampler: NeverSample(), input: mustTS(trace.ParseTraceState("k1=v1")), want: mustTS(trace.ParseTraceState("k1=v1")), exportSpan: false, }, { name: "insertKeySampled", sampler: makeInserter("k2", "v2", "span"), spanName: "span0", input: mustTS(trace.ParseTraceState("k1=v1")), want: mustTS(trace.ParseTraceState("k2=v2,k1=v1")), exportSpan: true, }, { name: "insertKeyDropped", sampler: makeInserter("k2", "v2", "span"), spanName: "nospan0", input: mustTS(trace.ParseTraceState("k1=v1")), want: mustTS(trace.ParseTraceState("k2=v2,k1=v1")), exportSpan: false, }, { name: "deleteKeySampled", sampler: makeDeleter("k1", "span"), spanName: "span0", input: mustTS(trace.ParseTraceState("k1=v1,k2=v2")), want: mustTS(trace.ParseTraceState("k2=v2")), exportSpan: true, }, { name: "deleteKeyDropped", sampler: makeDeleter("k1", "span"), spanName: "nospan0", input: mustTS(trace.ParseTraceState("k1=v1,k2=v2,k3=v3")), want: mustTS(trace.ParseTraceState("k2=v2,k3=v3")), exportSpan: false, }, { name: "clearer", sampler: clearer("span"), spanName: "span0", input: mustTS(trace.ParseTraceState("k1=v1,k3=v3")), want: mustTS(trace.ParseTraceState("")), exportSpan: true, }, } for _, ts := range tests { ts := ts t.Run(ts.name, func(t *testing.T) { te := NewTestExporter() tp := NewTracerProvider(WithSampler(ts.sampler), WithSyncer(te), WithResource(resource.Empty())) tr := tp.Tracer("TraceState") sc1 := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: trace.FlagsSampled, TraceState: ts.input, }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc1) _, span := tr.Start(ctx, ts.spanName) // span's TraceState should be set regardless of Sampled/NonSampled state. require.Equal(t, ts.want, span.SpanContext().TraceState()) span.End() got := te.Spans() if len(got) > 0 != ts.exportSpan { t.Errorf("unexpected number of exported spans %d", len(got)) } if len(got) == 0 { return } receivedState := got[0].SpanContext().TraceState() if diff := cmpDiff(receivedState, ts.want); diff != "" { t.Errorf("TraceState not propagated: -got +want %s", diff) } }) } } type testIDGenerator struct { traceID int spanID int } func (gen *testIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) { traceIDHex := fmt.Sprintf("%032x", gen.traceID) traceID, _ := trace.TraceIDFromHex(traceIDHex) gen.traceID++ spanID := gen.NewSpanID(ctx, traceID) return traceID, spanID } func (gen *testIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID { spanIDHex := fmt.Sprintf("%016x", gen.spanID) spanID, _ := trace.SpanIDFromHex(spanIDHex) gen.spanID++ return spanID } var _ IDGenerator = (*testIDGenerator)(nil) func TestWithIDGenerator(t *testing.T) { const ( startTraceID = 1 startSpanID = 1 numSpan = 10 ) gen := &testIDGenerator{traceID: startSpanID, spanID: startSpanID} for i := 0; i < numSpan; i++ { te := NewTestExporter() tp := NewTracerProvider( WithSyncer(te), WithIDGenerator(gen), ) span := startSpan(tp, "TestWithIDGenerator") got, err := strconv.ParseUint(span.SpanContext().SpanID().String(), 16, 64) require.NoError(t, err) want := uint64(startSpanID + i) assert.Equal(t, got, want) _, err = endSpan(te, span) require.NoError(t, err) } } golang-opentelemetry-otel-1.1.0/sdk/trace/tracer.go000066400000000000000000000115101414226744000223030ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "time" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/sdk/instrumentation" ) type tracer struct { provider *TracerProvider instrumentationLibrary instrumentation.Library } var _ trace.Tracer = &tracer{} // Start starts a Span and returns it along with a context containing it. // // The Span is created with the provided name and as a child of any existing // span context found in the passed context. The created Span will be // configured appropriately by any SpanOption passed. func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) { config := trace.NewSpanStartConfig(options...) // For local spans created by this SDK, track child span count. if p := trace.SpanFromContext(ctx); p != nil { if sdkSpan, ok := p.(*recordingSpan); ok { sdkSpan.addChild() } } s := tr.newSpan(ctx, name, &config) if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() { sps, _ := tr.provider.spanProcessors.Load().(spanProcessorStates) for _, sp := range sps { sp.sp.OnStart(ctx, rw) } } if rtt, ok := s.(runtimeTracer); ok { ctx = rtt.runtimeTrace(ctx) } return trace.ContextWithSpan(ctx, s), s } type runtimeTracer interface { // runtimeTrace starts a "runtime/trace".Task for the span and // returns a context containing the task. runtimeTrace(ctx context.Context) context.Context } // newSpan returns a new configured span. func (tr *tracer) newSpan(ctx context.Context, name string, config *trace.SpanConfig) trace.Span { // If told explicitly to make this a new root use a zero value SpanContext // as a parent which contains an invalid trace ID and is not remote. var psc trace.SpanContext if config.NewRoot() { ctx = trace.ContextWithSpanContext(ctx, psc) } else { psc = trace.SpanContextFromContext(ctx) } // If there is a valid parent trace ID, use it to ensure the continuity of // the trace. Always generate a new span ID so other components can rely // on a unique span ID, even if the Span is non-recording. var tid trace.TraceID var sid trace.SpanID if !psc.TraceID().IsValid() { tid, sid = tr.provider.idGenerator.NewIDs(ctx) } else { tid = psc.TraceID() sid = tr.provider.idGenerator.NewSpanID(ctx, tid) } samplingResult := tr.provider.sampler.ShouldSample(SamplingParameters{ ParentContext: ctx, TraceID: tid, Name: name, Kind: config.SpanKind(), Attributes: config.Attributes(), Links: config.Links(), }) scc := trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceState: samplingResult.Tracestate, } if isSampled(samplingResult) { scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled } else { scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled } sc := trace.NewSpanContext(scc) if !isRecording(samplingResult) { return tr.newNonRecordingSpan(sc) } return tr.newRecordingSpan(psc, sc, name, samplingResult, config) } // newRecordingSpan returns a new configured recordingSpan. func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr SamplingResult, config *trace.SpanConfig) *recordingSpan { startTime := config.Timestamp() if startTime.IsZero() { startTime = time.Now() } s := &recordingSpan{ parent: psc, spanContext: sc, spanKind: trace.ValidateSpanKind(config.SpanKind()), name: name, startTime: startTime, attributes: newAttributesMap(tr.provider.spanLimits.AttributeCountLimit), events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit), links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit), tracer: tr, spanLimits: tr.provider.spanLimits, resource: tr.provider.resource, instrumentationLibrary: tr.instrumentationLibrary, } for _, l := range config.Links() { s.addLink(l) } s.SetAttributes(sr.Attributes...) s.SetAttributes(config.Attributes()...) return s } // newNonRecordingSpan returns a new configured nonRecordingSpan. func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan { return nonRecordingSpan{tracer: tr, sc: sc} } golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/000077500000000000000000000000001414226744000224745ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/exporter.go000066400000000000000000000050601414226744000246740ustar00rootroot00000000000000// 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 tracetest is a testing helper package for the SDK. User can // configure no-op or in-memory exporters to verify different SDK behaviors or // custom instrumentation. package tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest" import ( "context" "sync" "go.opentelemetry.io/otel/sdk/trace" ) var _ trace.SpanExporter = (*NoopExporter)(nil) // NewNoopExporter returns a new no-op exporter. func NewNoopExporter() *NoopExporter { return new(NoopExporter) } // NoopExporter is an exporter that drops all received spans and performs no // action. type NoopExporter struct{} // ExportSpans handles export of spans by dropping them. func (nsb *NoopExporter) ExportSpans(context.Context, []trace.ReadOnlySpan) error { return nil } // Shutdown stops the exporter by doing nothing. func (nsb *NoopExporter) Shutdown(context.Context) error { return nil } var _ trace.SpanExporter = (*InMemoryExporter)(nil) // NewInMemoryExporter returns a new InMemoryExporter. func NewInMemoryExporter() *InMemoryExporter { return new(InMemoryExporter) } // InMemoryExporter is an exporter that stores all received spans in-memory. type InMemoryExporter struct { mu sync.Mutex ss SpanStubs } // ExportSpans handles export of spans by storing them in memory. func (imsb *InMemoryExporter) ExportSpans(_ context.Context, spans []trace.ReadOnlySpan) error { imsb.mu.Lock() defer imsb.mu.Unlock() imsb.ss = append(imsb.ss, SpanStubsFromReadOnlySpans(spans)...) return nil } // Shutdown stops the exporter by clearing spans held in memory. func (imsb *InMemoryExporter) Shutdown(context.Context) error { imsb.Reset() return nil } // Reset the current in-memory storage. func (imsb *InMemoryExporter) Reset() { imsb.mu.Lock() defer imsb.mu.Unlock() imsb.ss = nil } // GetSpans returns the current in-memory stored spans. func (imsb *InMemoryExporter) GetSpans() SpanStubs { imsb.mu.Lock() defer imsb.mu.Unlock() ret := make(SpanStubs, len(imsb.ss)) copy(ret, imsb.ss) return ret } golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/exporter_test.go000066400000000000000000000036301414226744000257340ustar00rootroot00000000000000// 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 tracetest import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestNoop tests only that the no-op does not crash in different scenarios. func TestNoop(t *testing.T) { nsb := NewNoopExporter() require.NoError(t, nsb.ExportSpans(context.Background(), nil)) require.NoError(t, nsb.ExportSpans(context.Background(), make(SpanStubs, 10).Snapshots())) require.NoError(t, nsb.ExportSpans(context.Background(), make(SpanStubs, 0, 10).Snapshots())) } func TestNewInMemoryExporter(t *testing.T) { imsb := NewInMemoryExporter() require.NoError(t, imsb.ExportSpans(context.Background(), nil)) assert.Len(t, imsb.GetSpans(), 0) input := make(SpanStubs, 10) for i := 0; i < 10; i++ { input[i] = SpanStub{Name: fmt.Sprintf("span %d", i)} } require.NoError(t, imsb.ExportSpans(context.Background(), input.Snapshots())) sds := imsb.GetSpans() assert.Len(t, sds, 10) for i, sd := range sds { assert.Equal(t, input[i], sd) } imsb.Reset() // Ensure that operations on the internal storage does not change the previously returned value. assert.Len(t, sds, 10) assert.Len(t, imsb.GetSpans(), 0) require.NoError(t, imsb.ExportSpans(context.Background(), input.Snapshots()[0:1])) sds = imsb.GetSpans() assert.Len(t, sds, 1) assert.Equal(t, input[0], sds[0]) } golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/recorder.go000066400000000000000000000047101414226744000246320ustar00rootroot00000000000000// 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 tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest" import ( "context" "sync" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) // SpanRecorder records started and ended spans. type SpanRecorder struct { startedMu sync.RWMutex started []sdktrace.ReadWriteSpan endedMu sync.RWMutex ended []sdktrace.ReadOnlySpan } var _ sdktrace.SpanProcessor = (*SpanRecorder)(nil) func NewSpanRecorder() *SpanRecorder { return new(SpanRecorder) } // OnStart records started spans. // // This method is safe to be called concurrently. func (sr *SpanRecorder) OnStart(_ context.Context, s sdktrace.ReadWriteSpan) { sr.startedMu.Lock() defer sr.startedMu.Unlock() sr.started = append(sr.started, s) } // OnEnd records completed spans. // // This method is safe to be called concurrently. func (sr *SpanRecorder) OnEnd(s sdktrace.ReadOnlySpan) { sr.endedMu.Lock() defer sr.endedMu.Unlock() sr.ended = append(sr.ended, s) } // Shutdown does nothing. // // This method is safe to be called concurrently. func (sr *SpanRecorder) Shutdown(context.Context) error { return nil } // ForceFlush does nothing. // // This method is safe to be called concurrently. func (sr *SpanRecorder) ForceFlush(context.Context) error { return nil } // Started returns a copy of all started spans that have been recorded. // // This method is safe to be called concurrently. func (sr *SpanRecorder) Started() []sdktrace.ReadWriteSpan { sr.startedMu.RLock() defer sr.startedMu.RUnlock() dst := make([]sdktrace.ReadWriteSpan, len(sr.started)) copy(dst, sr.started) return dst } // Ended returns a copy of all ended spans that have been recorded. // // This method is safe to be called concurrently. func (sr *SpanRecorder) Ended() []sdktrace.ReadOnlySpan { sr.endedMu.RLock() defer sr.endedMu.RUnlock() dst := make([]sdktrace.ReadOnlySpan, len(sr.ended)) copy(dst, sr.ended) return dst } golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/recorder_test.go000066400000000000000000000052721414226744000256750ustar00rootroot00000000000000// 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 tracetest import ( "context" "sync" "testing" "github.com/stretchr/testify/assert" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) type rwSpan struct { sdktrace.ReadWriteSpan } func TestSpanRecorderOnStartAppends(t *testing.T) { s0, s1 := new(rwSpan), new(rwSpan) ctx := context.Background() sr := new(SpanRecorder) assert.Len(t, sr.started, 0) sr.OnStart(ctx, s0) assert.Len(t, sr.started, 1) sr.OnStart(ctx, s1) assert.Len(t, sr.started, 2) // Ensure order correct. started := sr.Started() assert.Same(t, s0, started[0]) assert.Same(t, s1, started[1]) } type roSpan struct { sdktrace.ReadOnlySpan } func TestSpanRecorderOnEndAppends(t *testing.T) { s0, s1 := new(roSpan), new(roSpan) sr := new(SpanRecorder) assert.Len(t, sr.ended, 0) sr.OnEnd(s0) assert.Len(t, sr.ended, 1) sr.OnEnd(s1) assert.Len(t, sr.ended, 2) // Ensure order correct. ended := sr.Ended() assert.Same(t, s0, ended[0]) assert.Same(t, s1, ended[1]) } func TestSpanRecorderShutdownNoError(t *testing.T) { ctx := context.Background() assert.NoError(t, new(SpanRecorder).Shutdown(ctx)) var c context.CancelFunc ctx, c = context.WithCancel(ctx) c() assert.NoError(t, new(SpanRecorder).Shutdown(ctx)) } func TestSpanRecorderForceFlushNoError(t *testing.T) { ctx := context.Background() assert.NoError(t, new(SpanRecorder).ForceFlush(ctx)) var c context.CancelFunc ctx, c = context.WithCancel(ctx) c() assert.NoError(t, new(SpanRecorder).ForceFlush(ctx)) } func runConcurrently(funcs ...func()) { var wg sync.WaitGroup for _, f := range funcs { wg.Add(1) go func(f func()) { f() wg.Done() }(f) } wg.Wait() } func TestEndingConcurrency(t *testing.T) { sr := NewSpanRecorder() runConcurrently( func() { sr.OnEnd(new(roSpan)) }, func() { sr.OnEnd(new(roSpan)) }, func() { sr.Ended() }, ) assert.Len(t, sr.Ended(), 2) } func TestStartingConcurrency(t *testing.T) { sr := NewSpanRecorder() ctx := context.Background() runConcurrently( func() { sr.OnStart(ctx, new(rwSpan)) }, func() { sr.OnStart(ctx, new(rwSpan)) }, func() { sr.Started() }, ) assert.Len(t, sr.Started(), 2) } golang-opentelemetry-otel-1.1.0/sdk/trace/tracetest/span.go000066400000000000000000000132431414226744000237670ustar00rootroot00000000000000// 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 tracetest // import "go.opentelemetry.io/otel/sdk/trace/tracetest" import ( "time" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" ) type SpanStubs []SpanStub // SpanStubsFromReadOnlySpans returns SpanStubs populated from ro. func SpanStubsFromReadOnlySpans(ro []tracesdk.ReadOnlySpan) SpanStubs { if len(ro) == 0 { return nil } s := make(SpanStubs, 0, len(ro)) for _, r := range ro { s = append(s, SpanStubFromReadOnlySpan(r)) } return s } // Snapshots returns s as a slice of ReadOnlySpans. func (s SpanStubs) Snapshots() []tracesdk.ReadOnlySpan { if len(s) == 0 { return nil } ro := make([]tracesdk.ReadOnlySpan, len(s)) for i := 0; i < len(s); i++ { ro[i] = s[i].Snapshot() } return ro } // SpanStub is a stand-in for a Span. type SpanStub struct { Name string SpanContext trace.SpanContext Parent trace.SpanContext SpanKind trace.SpanKind StartTime time.Time EndTime time.Time Attributes []attribute.KeyValue Events []tracesdk.Event Links []tracesdk.Link Status tracesdk.Status DroppedAttributes int DroppedEvents int DroppedLinks int ChildSpanCount int Resource *resource.Resource InstrumentationLibrary instrumentation.Library } // SpanStubFromReadOnlySpan returns a SpanStub populated from ro. func SpanStubFromReadOnlySpan(ro tracesdk.ReadOnlySpan) SpanStub { if ro == nil { return SpanStub{} } return SpanStub{ Name: ro.Name(), SpanContext: ro.SpanContext(), Parent: ro.Parent(), SpanKind: ro.SpanKind(), StartTime: ro.StartTime(), EndTime: ro.EndTime(), Attributes: ro.Attributes(), Events: ro.Events(), Links: ro.Links(), Status: ro.Status(), DroppedAttributes: ro.DroppedAttributes(), DroppedEvents: ro.DroppedEvents(), DroppedLinks: ro.DroppedLinks(), ChildSpanCount: ro.ChildSpanCount(), Resource: ro.Resource(), InstrumentationLibrary: ro.InstrumentationLibrary(), } } // Snapshot returns a read-only copy of the SpanStub. func (s SpanStub) Snapshot() tracesdk.ReadOnlySpan { return spanSnapshot{ name: s.Name, spanContext: s.SpanContext, parent: s.Parent, spanKind: s.SpanKind, startTime: s.StartTime, endTime: s.EndTime, attributes: s.Attributes, events: s.Events, links: s.Links, status: s.Status, droppedAttributes: s.DroppedAttributes, droppedEvents: s.DroppedEvents, droppedLinks: s.DroppedLinks, childSpanCount: s.ChildSpanCount, resource: s.Resource, instrumentationLibrary: s.InstrumentationLibrary, } } type spanSnapshot struct { // Embed the interface to implement the private method. tracesdk.ReadOnlySpan name string spanContext trace.SpanContext parent trace.SpanContext spanKind trace.SpanKind startTime time.Time endTime time.Time attributes []attribute.KeyValue events []tracesdk.Event links []tracesdk.Link status tracesdk.Status droppedAttributes int droppedEvents int droppedLinks int childSpanCount int resource *resource.Resource instrumentationLibrary instrumentation.Library } func (s spanSnapshot) Name() string { return s.name } func (s spanSnapshot) SpanContext() trace.SpanContext { return s.spanContext } func (s spanSnapshot) Parent() trace.SpanContext { return s.parent } func (s spanSnapshot) SpanKind() trace.SpanKind { return s.spanKind } func (s spanSnapshot) StartTime() time.Time { return s.startTime } func (s spanSnapshot) EndTime() time.Time { return s.endTime } func (s spanSnapshot) Attributes() []attribute.KeyValue { return s.attributes } func (s spanSnapshot) Links() []tracesdk.Link { return s.links } func (s spanSnapshot) Events() []tracesdk.Event { return s.events } func (s spanSnapshot) Status() tracesdk.Status { return s.status } func (s spanSnapshot) DroppedAttributes() int { return s.droppedAttributes } func (s spanSnapshot) DroppedLinks() int { return s.droppedLinks } func (s spanSnapshot) DroppedEvents() int { return s.droppedEvents } func (s spanSnapshot) ChildSpanCount() int { return s.childSpanCount } func (s spanSnapshot) Resource() *resource.Resource { return s.resource } func (s spanSnapshot) InstrumentationLibrary() instrumentation.Library { return s.instrumentationLibrary } golang-opentelemetry-otel-1.1.0/sdk/trace/util_test.go000066400000000000000000000015211414226744000230400ustar00rootroot00000000000000// 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 trace_test import ( "testing" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) func basicTracerProvider(t *testing.T) *sdktrace.TracerProvider { tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample())) return tp } golang-opentelemetry-otel-1.1.0/semconv/000077500000000000000000000000001414226744000202715ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/semconv/template.j2000066400000000000000000000050151414226744000223420ustar00rootroot00000000000000{%- macro to_go_attr_type(type, val) -%} {%- if type == "string" -%} String("{{val}}") {%- elif type == "int" -%} Int({{val}}) {%- endif -%} {%- endmacro -%} {%- macro to_go_name(fqn) -%} {{fqn | replace(".", " ") | replace("_", " ") | title | replace(" ", "")}} {%- endmacro -%} {%- macro godoc(attr) -%} {{ attr.brief }} // {%- if attr.attr_type is string %} Type: {{ attr.attr_type }} {%- else %} Type: Enum {%- endif %} {%- if attr.required == Required.ALWAYS %} Required: Always {%- elif attr.required == Required.CONDITIONAL %} Required: {{ attr.required_msg }} {%- else %} Required: No {%- endif %} {{ attr.stability | replace("Level.", ": ") | capitalize }} {%- if attr.deprecated != None %} Deprecated: {{ attr.deprecated }} {%- endif %} {%- if attr.examples is iterable %} Examples: {{ attr.examples | pprint | trim("[]") }} {%- endif %} {%- if attr.note %} Note: {{ attr.note }} {%- endif %} {%- endmacro -%} // 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import [[IMPORTPATH]] import "go.opentelemetry.io/otel/attribute" {% for semconv in semconvs -%} {%- if semconvs[semconv].attributes | rejectattr("ref") | selectattr("is_local") | sort(attribute=fqn) | length > 0 -%} // {{ semconvs[semconv].brief }} const ( {% for attr in semconvs[semconv].attributes if attr.is_local and not attr.ref -%} // {{ godoc(attr) | wordwrap | indent(3) | replace(" ", "\t// ") | replace("// //", "//") }} {{to_go_name(attr.fqn)}}Key = attribute.Key("{{attr.fqn}}") {% endfor %} ) {%- for attr in semconvs[semconv].attributes if attr.is_local and not attr.ref -%} {%- if attr.attr_type is not string %} var ( {%- for val in attr.attr_type.members %} // {{ val.brief | to_doc_brief }} {{to_go_name("{}.{}".format(attr.fqn, val.member_id))}} = {{to_go_name(attr.fqn)}}Key.{{to_go_attr_type(attr.attr_type.enum_type, val.value)}} {%- endfor %} ) {%- endif -%} {%- endfor %} {% endif %} {% endfor -%} golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/000077500000000000000000000000001414226744000211175ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/doc.go000066400000000000000000000016621414226744000222200ustar00rootroot00000000000000// 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 semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions // as of the v1.4.0 version of the OpenTelemetry specification. package semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/exception.go000066400000000000000000000014271414226744000234500ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" const ( // ExceptionEventName is the name of the Span event representing an exception. ExceptionEventName = "exception" ) golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/http.go000066400000000000000000000217301414226744000224300ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" import ( "fmt" "net" "net/http" "strconv" "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // HTTP scheme attributes. var ( HTTPSchemeHTTP = HTTPSchemeKey.String("http") HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} switch network { case "tcp", "tcp4", "tcp6": attrs = append(attrs, NetTransportTCP) case "udp", "udp4", "udp6": attrs = append(attrs, NetTransportUDP) case "ip", "ip4", "ip6": attrs = append(attrs, NetTransportIP) case "unix", "unixgram", "unixpacket": attrs = append(attrs, NetTransportUnix) default: attrs = append(attrs, NetTransportOther) } peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) if peerIP != "" { attrs = append(attrs, NetPeerIPKey.String(peerIP)) } if peerName != "" { attrs = append(attrs, NetPeerNameKey.String(peerName)) } if peerPort != 0 { attrs = append(attrs, NetPeerPortKey.Int(peerPort)) } hostIP, hostName, hostPort := "", "", 0 for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { hostIP, hostName, hostPort = hostIPNamePort(someHost) if hostIP != "" || hostName != "" || hostPort != 0 { break } } if hostIP != "" { attrs = append(attrs, NetHostIPKey.String(hostIP)) } if hostName != "" { attrs = append(attrs, NetHostNameKey.String(hostName)) } if hostPort != 0 { attrs = append(attrs, NetHostPortKey.Int(hostPort)) } return attrs } // hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. // It handles both IPv4 and IPv6 addresses. If the host portion is not recognized // as a valid IPv4 or IPv6 address, the `ip` result will be empty and the // host portion will instead be returned in `name`. func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { var ( hostPart, portPart string parsedPort uint64 err error ) if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { hostPart, portPart = hostWithPort, "" } if parsedIP := net.ParseIP(hostPart); parsedIP != nil { ip = parsedIP.String() } else { name = hostPart } if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { port = int(parsedPort) } return } // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { if username, _, ok := request.BasicAuth(); ok { return []attribute.KeyValue{EnduserIDKey.String(username)} } return nil } // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if request.Method != "" { attrs = append(attrs, HTTPMethodKey.String(request.Method)) } else { attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) } // remove any username/password info that may be in the URL // before adding it to the attributes userinfo := request.URL.User request.URL.User = nil attrs = append(attrs, HTTPURLKey.String(request.URL.String())) // restore any username/password info that was removed request.URL.User = userinfo return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if ua := request.UserAgent(); ua != "" { attrs = append(attrs, HTTPUserAgentKey.String(ua)) } if request.ContentLength > 0 { attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality attrs := []attribute.KeyValue{} if request.TLS != nil { attrs = append(attrs, HTTPSchemeHTTPS) } else { attrs = append(attrs, HTTPSchemeHTTP) } if request.Host != "" { attrs = append(attrs, HTTPHostKey.String(request.Host)) } flavor := "" if request.ProtoMajor == 1 { flavor = fmt.Sprintf("1.%d", request.ProtoMinor) } else if request.ProtoMajor == 2 { flavor = "2" } if flavor != "" { attrs = append(attrs, HTTPFlavorKey.String(flavor)) } return attrs } // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } // HTTPServerAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the server side. Currently, only basic authentication is // supported. func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPMethodKey.String(request.Method), HTTPTargetKey.String(request.RequestURI), } if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } if route != "" { attrs = append(attrs, HTTPRouteKey.String(route)) } if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) } } return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPStatusCodeKey.Int(code), } return attrs } type codeRange struct { fromInclusive int toInclusive int } func (r codeRange) contains(code int) bool { return r.fromInclusive <= code && code <= r.toInclusive } var validRangesPerCategory = map[int][]codeRange{ 1: { {http.StatusContinue, http.StatusEarlyHints}, }, 2: { {http.StatusOK, http.StatusAlreadyReported}, {http.StatusIMUsed, http.StatusIMUsed}, }, 3: { {http.StatusMultipleChoices, http.StatusUseProxy}, {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, }, 4: { {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, {http.StatusPreconditionRequired, http.StatusTooManyRequests}, {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, }, 5: { {http.StatusInternalServerError, http.StatusLoopDetected}, {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, }, } // SpanStatusFromHTTPStatusCode generates a status code and a message // as specified by the OpenTelemetry specification for a span. func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { spanCode, valid := validateHTTPStatusCode(code) if !valid { return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) } return spanCode, "" } // Validates the HTTP status code and returns corresponding span status code. // If the `code` is not a valid HTTP status code, returns span status Error // and false. func validateHTTPStatusCode(code int) (codes.Code, bool) { category := code / 100 ranges, ok := validRangesPerCategory[category] if !ok { return codes.Error, false } ok = false for _, crange := range ranges { ok = crange.contains(code) if ok { break } } if !ok { return codes.Error, false } if category > 0 && category < 4 { return codes.Unset, true } return codes.Error, true } golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/http_test.go000066400000000000000000000725421414226744000234760ustar00rootroot00000000000000// 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 semconv import ( "crypto/tls" "net/http" "net/url" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) type tlsOption int const ( noTLS tlsOption = iota withTLS ) func TestNetAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string network string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped, tcp", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), }, }, { name: "stripped, udp", network: "udp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_udp"), }, }, { name: "stripped, ip", network: "ip", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip"), }, }, { name: "stripped, unix", network: "unix", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "unix"), }, }, { name: "stripped, other", network: "nih", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "other"), }, }, { name: "with remote ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[fe80::0202:b3ff:fe1e:8329]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4-in-v6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[::ffff:192.168.0.1]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), }, }, { name: "with remote ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "fe80::0202:b3ff:fe1e:8329", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with remote ipv4_in_v6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "::ffff:192.168.0.1", // section 2.5.5.2 of RFC4291 host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), }, }, { name: "with remote name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), }, }, { name: "with remote port only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: ":56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.Int("net.peer.port", 56), }, }, { name: "with host name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "fe80::0202:b3ff:fe1e:8329", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with host name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, { name: "with host name and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with empty host and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: ":80", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.Int("net.host.port", 80), }, }, { name: "with host ip and port in headers", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "Host": []string{"4.3.2.1:78"}, }, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "4.3.2.1:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "[fe80::202:b3ff:fe1e:8329]:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS) got := NetAttributesFromHTTPRequest(tc.network, r) if diff := cmp.Diff( tc.expected, got, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("attributes differ: diff %+v,", diff) } }) } } func TestEndUserAttributesFromHTTPRequest(t *testing.T) { r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS) var expected []attribute.KeyValue got := EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) r.SetBasicAuth("admin", "password") expected = []attribute.KeyValue{attribute.String("enduser.id", "admin")} got = EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) } func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string serverName string route string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped", serverName: "", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with server name", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with tls", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with route", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), }, }, { name: "with host", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with proxy info", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"203.0.113.195, 70.41.3.18, 150.172.238.178"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "203.0.113.195"), }, }, { name: "with http 1.1", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with http 2", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with content length", method: "GET", requestURI: "/user/123", contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, } for idx, tc := range testcases { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r) assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) } } func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) { expected := []attribute.KeyValue{ attribute.Int("http.status_code", 404), } got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound) assertElementsMatch(t, expected, got, "with valid HTTP status code") assert.ElementsMatch(t, expected, got) expected = []attribute.KeyValue{ attribute.Int("http.status_code", 499), } got = HTTPAttributesFromHTTPStatusCode(499) assertElementsMatch(t, expected, got, "with invalid HTTP status code") } func TestSpanStatusFromHTTPStatusCode(t *testing.T) { for code := 0; code < 1000; code++ { expected := getExpectedCodeForHTTPCode(code) got, msg := SpanStatusFromHTTPStatusCode(code) assert.Equalf(t, expected, got, "%s vs %s", expected, got) _, valid := validateHTTPStatusCode(code) if !valid { assert.NotEmpty(t, msg, "message should be set if error cannot be inferred from code") } else { assert.Empty(t, msg, "message should not be set if error can be inferred from code") } } } func getExpectedCodeForHTTPCode(code int) codes.Code { if http.StatusText(code) == "" { return codes.Error } switch code { case http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusTooManyRequests, http.StatusNotImplemented, http.StatusServiceUnavailable, http.StatusGatewayTimeout: return codes.Error } category := code / 100 if category > 0 && category < 4 { return codes.Unset } return codes.Error } func assertElementsMatch(t *testing.T, expected, got []attribute.KeyValue, format string, args ...interface{}) { if !assert.ElementsMatchf(t, expected, got, format, args...) { t.Log("expected:", kvStr(expected)) t.Log("got:", kvStr(got)) } } func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request { major, minor := protoToInts(proto) var tlsConn *tls.ConnectionState switch tlsopt { case noTLS: case withTLS: tlsConn = &tls.ConnectionState{} } return &http.Request{ Method: method, URL: u, Proto: proto, ProtoMajor: major, ProtoMinor: minor, Header: header, Host: host, RemoteAddr: remoteAddr, RequestURI: requestURI, TLS: tlsConn, } } func protoToInts(proto string) (int, int) { switch proto { case "HTTP/1.0": return 1, 0 case "HTTP/1.1": return 1, 1 case "HTTP/2.0": return 2, 0 } // invalid proto return 13, 42 } func kvStr(kvs []attribute.KeyValue) string { sb := strings.Builder{} sb.WriteRune('[') for idx, label := range kvs { if idx > 0 { sb.WriteString(", ") } sb.WriteString((string)(label.Key)) sb.WriteString(": ") sb.WriteString(label.Value.Emit()) } sb.WriteRune(']') return sb.String() } func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) { testCases := []struct { name string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue }{ { name: "stripped", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with tls", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), }, }, { name: "with host", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 1.1", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 2", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with content length", method: "GET", url: &url.URL{ Path: "/user/123", }, contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, { name: "with empty method (fallback to GET)", method: "", url: &url.URL{ Path: "/user/123", }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, { name: "authentication information is stripped", method: "", url: &url.URL{ Path: "/user/123", User: url.UserPassword("foo", "bar"), }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPClientAttributesFromHTTPRequest(r) assert.ElementsMatch(t, tc.expected, got) }) } } golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/resource.go000066400000000000000000000717531414226744000233120ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" import "go.opentelemetry.io/otel/attribute" // A cloud environment (e.g. GCP, Azure, AWS) const ( // Name of the cloud provider. // // Type: Enum // Required: No // Stability: stable // Examples: 'gcp' CloudProviderKey = attribute.Key("cloud.provider") // The cloud account ID the resource is assigned to. // // Type: string // Required: No // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") // The geographical region the resource is running. Refer to your provider's docs // to see the available regions, for example [AWS // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), // [Azure regions](https://azure.microsoft.com/en-us/global- // infrastructure/geographies/), or [Google Cloud // regions](https://cloud.google.com/about/locations). // // Type: string // Required: No // Stability: stable // Examples: 'us-central1', 'us-east-1' CloudRegionKey = attribute.Key("cloud.region") // Cloud regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the resource // is running. // // Type: string // Required: No // Stability: stable // Examples: 'us-east-1c' // Note: Availability zones are called "zones" on Google Cloud. CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") // The cloud platform in use. // // Type: Enum // Required: No // Stability: stable // Examples: 'aws_ec2', 'azure_vm', 'gcp_compute_engine' // Note: The prefix of the service SHOULD match the one specified in // `cloud.provider`. CloudPlatformKey = attribute.Key("cloud.platform") ) var ( // Amazon Web Services CloudProviderAWS = CloudProviderKey.String("aws") // Microsoft Azure CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") ) var ( // AWS Elastic Compute Cloud CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") // AWS Elastic Container Service CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") // AWS Elastic Kubernetes Service CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") // AWS Lambda CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") // Azure Kubernetes Service CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") // Azure Functions CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") // Azure App Service CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") // Google Cloud Compute Engine (GCE) CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") // Google Cloud Run CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") // Google Cloud Kubernetes Engine (GKE) CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") // Google Cloud Functions (GCF) CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") ) // Resources used by AWS Elastic Container Service (ECS). const ( // The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws. // amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") // The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo // perguide/clusters.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") // The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l // aunch_types.html) for an ECS task. // // Type: Enum // Required: No // Stability: stable // Examples: 'ec2', 'fargate' AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") // The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates // t/developerguide/task_definitions.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") // The task definition family this task definition is a member of. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-family' AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") // The revision for this task definition. // // Type: string // Required: No // Stability: stable // Examples: '8', '26' AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") ) var ( // ec2 AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") // fargate AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") ) // Resources used by AWS Elastic Kubernetes Service (EKS). const ( // The ARN of an EKS cluster. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") ) // Resources specific to Amazon Web Services. const ( // The name(s) of the AWS log group(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: '/aws/lambda/my-function', 'opentelemetry-service' // Note: Multiple log groups must be supported for cases like multi-container // applications, where a single application has sidecar containers, and each write // to their own log group. AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") // The Amazon Resource Name(s) (ARN) of the AWS log group(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' // Note: See the [log group ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") // The name(s) of the AWS log stream(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") // The ARN(s) of the AWS log stream(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log- // stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' // Note: See the [log stream ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain // several log streams, so these ARNs necessarily identify both a log group and a // log stream. AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") ) // A container instance. const ( // Container name. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-autoconf' ContainerNameKey = attribute.Key("container.name") // Container ID. Usually a UUID, as for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container- // identification). The UUID might be abbreviated. // // Type: string // Required: No // Stability: stable // Examples: 'a3bf90e006b2' ContainerIDKey = attribute.Key("container.id") // The container runtime managing this container. // // Type: string // Required: No // Stability: stable // Examples: 'docker', 'containerd', 'rkt' ContainerRuntimeKey = attribute.Key("container.runtime") // Name of the image the container was built on. // // Type: string // Required: No // Stability: stable // Examples: 'gcr.io/opentelemetry/operator' ContainerImageNameKey = attribute.Key("container.image.name") // Container image tag. // // Type: string // Required: No // Stability: stable // Examples: '0.1' ContainerImageTagKey = attribute.Key("container.image.tag") ) // The software deployment. const ( // Name of the [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). // // Type: string // Required: No // Stability: stable // Examples: 'staging', 'production' DeploymentEnvironmentKey = attribute.Key("deployment.environment") ) // The device on which the process represented by this resource is running. const ( // A unique identifier representing the device // // Type: string // Required: No // Stability: stable // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' // Note: The device identifier MUST only be defined using the values outlined // below. This value is not an advertising identifier and MUST NOT be used as // such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id // entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden // tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the // Firebase Installation ID or a globally unique UUID which is persisted across // sessions in your application. More information can be found // [here](https://developer.android.com/training/articles/user-data-ids) on best // practices and exact implementation details. Caution should be taken when // storing personal data or anything which can identify a user. GDPR and data // protection laws may apply, ensure you do your own due diligence. DeviceIDKey = attribute.Key("device.id") // The model identifier for the device // // Type: string // Required: No // Stability: stable // Examples: 'iPhone3,4', 'SM-G920F' // Note: It's recommended this value represents a machine readable version of the // model identifier rather than the market or consumer-friendly name of the // device. DeviceModelIdentifierKey = attribute.Key("device.model.identifier") // The marketing name for the device model // // Type: string // Required: No // Stability: stable // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' // Note: It's recommended this value represents a human readable version of the // device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") ) // A serverless instance. const ( // The name of the function being executed. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' FaaSNameKey = attribute.Key("faas.name") // The unique ID of the function being executed. // // Type: string // Required: Always // Stability: stable // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' // Note: For example, in AWS Lambda this field corresponds to the // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and- // namespaces.html) value, in GCP to the URI of the resource, and in Azure to the // [FunctionDirectory](https://github.com/Azure/azure-functions- // host/wiki/Retrieving-information-about-the-currently-running-function) field. FaaSIDKey = attribute.Key("faas.id") // The version string of the function being executed as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '2.0.0' FaaSVersionKey = attribute.Key("faas.version") // The execution environment ID as a string. // // Type: string // Required: No // Stability: stable // Examples: 'my-function:instance-0001' FaaSInstanceKey = attribute.Key("faas.instance") // The amount of memory available to the serverless function in MiB. // // Type: int // Required: No // Stability: stable // Examples: 128 // Note: It's recommended to set this attribute since e.g. too little memory can // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this // information. FaaSMaxMemoryKey = attribute.Key("faas.max_memory") ) // A host is defined as a general computing instance. const ( // Unique host ID. For Cloud, this must be the instance_id assigned by the cloud // provider. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostIDKey = attribute.Key("host.id") // Name of the host. On Unix systems, it may contain what the hostname command // returns, or the fully qualified hostname, or another name specified by the // user. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostNameKey = attribute.Key("host.name") // Type of host. For Cloud, this must be the machine type. // // Type: string // Required: No // Stability: stable // Examples: 'n1-standard-1' HostTypeKey = attribute.Key("host.type") // The CPU architecture the host system is running on. // // Type: Enum // Required: No // Stability: stable HostArchKey = attribute.Key("host.arch") // Name of the VM image or OS install the host was instantiated from. // // Type: string // Required: No // Stability: stable // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' HostImageNameKey = attribute.Key("host.image.name") // VM image ID. For Cloud, this value is from the provider. // // Type: string // Required: No // Stability: stable // Examples: 'ami-07b06b442921831e5' HostImageIDKey = attribute.Key("host.image.id") // The version string of the VM image as defined in [Version // Attributes](README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '0.1' HostImageVersionKey = attribute.Key("host.image.version") ) var ( // AMD64 HostArchAMD64 = HostArchKey.String("amd64") // ARM32 HostArchARM32 = HostArchKey.String("arm32") // ARM64 HostArchARM64 = HostArchKey.String("arm64") // Itanium HostArchIA64 = HostArchKey.String("ia64") // 32-bit PowerPC HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) // A Kubernetes Cluster. const ( // The name of the cluster. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-cluster' K8SClusterNameKey = attribute.Key("k8s.cluster.name") ) // A Kubernetes Node object. const ( // The name of the Node. // // Type: string // Required: No // Stability: stable // Examples: 'node-1' K8SNodeNameKey = attribute.Key("k8s.node.name") // The UID of the Node. // // Type: string // Required: No // Stability: stable // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' K8SNodeUIDKey = attribute.Key("k8s.node.uid") ) // A Kubernetes Namespace. const ( // The name of the namespace that the pod is running in. // // Type: string // Required: No // Stability: stable // Examples: 'default' K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") ) // A Kubernetes Pod object. const ( // The UID of the Pod. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SPodUIDKey = attribute.Key("k8s.pod.uid") // The name of the Pod. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-pod-autoconf' K8SPodNameKey = attribute.Key("k8s.pod.name") ) // A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( // The name of the Container in a Pod template. // // Type: string // Required: No // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") ) // A Kubernetes ReplicaSet object. const ( // The UID of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SReplicasetUIDKey = attribute.Key("k8s.replicaset.uid") // The name of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SReplicasetNameKey = attribute.Key("k8s.replicaset.name") ) // A Kubernetes Deployment object. const ( // The UID of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") // The name of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") ) // A Kubernetes StatefulSet object. const ( // The UID of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SStatefulsetUIDKey = attribute.Key("k8s.statefulset.uid") // The name of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SStatefulsetNameKey = attribute.Key("k8s.statefulset.name") ) // A Kubernetes DaemonSet object. const ( // The UID of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDaemonsetUIDKey = attribute.Key("k8s.daemonset.uid") // The name of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDaemonsetNameKey = attribute.Key("k8s.daemonset.name") ) // A Kubernetes Job object. const ( // The UID of the Job. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SJobUIDKey = attribute.Key("k8s.job.uid") // The name of the Job. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SJobNameKey = attribute.Key("k8s.job.name") ) // A Kubernetes CronJob object. const ( // The UID of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") // The name of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") ) // The operating system (OS) on which the process represented by this resource is running. const ( // The operating system type. // // Type: Enum // Required: Always // Stability: stable OSTypeKey = attribute.Key("os.type") // Human readable (not intended to be parsed) OS version information, like e.g. // reported by `ver` or `lsb_release -a` commands. // // Type: string // Required: No // Stability: stable // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS' OSDescriptionKey = attribute.Key("os.description") // Human readable operating system name. // // Type: string // Required: No // Stability: stable // Examples: 'iOS', 'Android', 'Ubuntu' OSNameKey = attribute.Key("os.name") // The version string of the operating system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '14.2.1', '18.04.1' OSVersionKey = attribute.Key("os.version") ) var ( // Microsoft Windows OSTypeWindows = OSTypeKey.String("windows") // Linux OSTypeLinux = OSTypeKey.String("linux") // Apple Darwin OSTypeDarwin = OSTypeKey.String("darwin") // FreeBSD OSTypeFreeBSD = OSTypeKey.String("freebsd") // NetBSD OSTypeNetBSD = OSTypeKey.String("netbsd") // OpenBSD OSTypeOpenBSD = OSTypeKey.String("openbsd") // DragonFly BSD OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") // HP-UX (Hewlett Packard Unix) OSTypeHPUX = OSTypeKey.String("hpux") // AIX (Advanced Interactive eXecutive) OSTypeAIX = OSTypeKey.String("aix") // Oracle Solaris OSTypeSolaris = OSTypeKey.String("solaris") // IBM z/OS OSTypeZOS = OSTypeKey.String("z_os") ) // An operating system process. const ( // Process identifier (PID). // // Type: int // Required: No // Stability: stable // Examples: 1234 ProcessPIDKey = attribute.Key("process.pid") // The name of the process executable. On Linux based systems, can be set to the // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: 'otelcol' ProcessExecutableNameKey = attribute.Key("process.executable.name") // The full path to the process executable. On Linux based systems, can be set to // the target of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: '/usr/bin/cmd/otelcol' ProcessExecutablePathKey = attribute.Key("process.executable.path") // The command used to launch the process (i.e. the command name). On Linux based // systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, // can be set to the first parameter extracted from `GetCommandLineW`. // // Type: string // Required: See below // Stability: stable // Examples: 'cmd/otelcol' ProcessCommandKey = attribute.Key("process.command") // The full command used to launch the process as a single string representing the // full command. On Windows, can be set to the result of `GetCommandLineW`. Do not // set this if you have to assemble it just for monitoring; use // `process.command_args` instead. // // Type: string // Required: See below // Stability: stable // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' ProcessCommandLineKey = attribute.Key("process.command_line") // All the command arguments (including the command/executable itself) as received // by the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited strings // extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be // the full argv vector passed to `main`. // // Type: string[] // Required: See below // Stability: stable // Examples: 'cmd/otecol', '--config=config.yaml' ProcessCommandArgsKey = attribute.Key("process.command_args") // The username of the user that owns the process. // // Type: string // Required: No // Stability: stable // Examples: 'root' ProcessOwnerKey = attribute.Key("process.owner") ) // The single (language) runtime instance which is monitored. const ( // The name of the runtime of this process. For compiled native binaries, this // SHOULD be the name of the compiler. // // Type: string // Required: No // Stability: stable // Examples: 'OpenJDK Runtime Environment' ProcessRuntimeNameKey = attribute.Key("process.runtime.name") // The version of the runtime of this process, as returned by the runtime without // modification. // // Type: string // Required: No // Stability: stable // Examples: '14.0.2' ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") // An additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. // // Type: string // Required: No // Stability: stable // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") ) // A service instance. const ( // Logical name of the service. // // Type: string // Required: Always // Stability: stable // Examples: 'shoppingcart' // Note: MUST be the same for all instances of horizontally scaled services. If // the value was not specified, SDKs MUST fallback to `unknown_service:` // concatenated with [`process.executable.name`](process.md#process), e.g. // `unknown_service:bash`. If `process.executable.name` is not available, the // value MUST be set to `unknown_service`. ServiceNameKey = attribute.Key("service.name") // A namespace for `service.name`. // // Type: string // Required: No // Stability: stable // Examples: 'Shop' // Note: A string value having a meaning that helps to distinguish a group of // services, for example the team name that owns a group of services. // `service.name` is expected to be unique within the same namespace. If // `service.namespace` is not specified in the Resource then `service.name` is // expected to be unique for all services that have no explicit namespace defined // (so the empty/unspecified namespace is simply one more valid namespace). Zero- // length namespace string is assumed equal to unspecified namespace. ServiceNamespaceKey = attribute.Key("service.namespace") // The string ID of the service instance. // // Type: string // Required: No // Stability: stable // Examples: '627cc493-f310-47de-96bd-71410b7dec09' // Note: MUST be unique for each instance of the same // `service.namespace,service.name` pair (in other words // `service.namespace,service.name,service.instance.id` triplet MUST be globally // unique). The ID helps to distinguish instances of the same service that exist // at the same time (e.g. instances of a horizontally scaled service). It is // preferable for the ID to be persistent and stay the same for the lifetime of // the service instance, however it is acceptable that the ID is ephemeral and // changes during important lifetime events for the service (e.g. service // restarts). If the service has no inherent unique ID that can be used as the // value of this attribute it is recommended to generate a random Version 1 or // Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use // Version 5, see RFC 4122 for more recommendations). ServiceInstanceIDKey = attribute.Key("service.instance.id") // The version string of the service API or implementation. // // Type: string // Required: No // Stability: stable // Examples: '2.0.0' ServiceVersionKey = attribute.Key("service.version") ) // The telemetry SDK used to capture data recorded by the instrumentation libraries. const ( // The name of the telemetry SDK as defined above. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") // The language of the telemetry SDK. // // Type: Enum // Required: No // Stability: stable TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") // The version string of the telemetry SDK. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") // The version string of the auto instrumentation agent, if used. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") ) var ( // cpp TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") // dotnet TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") // erlang TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") // go TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") // java TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") // nodejs TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") // php TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") // python TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") // ruby TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") ) // Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. const ( // The name of the web engine. // // Type: string // Required: Always // Stability: stable // Examples: 'WildFly' WebEngineNameKey = attribute.Key("webengine.name") // The version of the web engine. // // Type: string // Required: No // Stability: stable // Examples: '21.0.0' WebEngineVersionKey = attribute.Key("webengine.version") // Additional description of the web engine (e.g. detailed version and edition // information). // // Type: string // Required: No // Stability: stable // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final' WebEngineDescriptionKey = attribute.Key("webengine.description") ) golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/schema.go000066400000000000000000000017131414226744000227100ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ const SchemaURL = "https://opentelemetry.io/schemas/v1.4.0" golang-opentelemetry-otel-1.1.0/semconv/v1.4.0/trace.go000066400000000000000000001371651414226744000225610ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" import "go.opentelemetry.io/otel/attribute" // This document defines the attributes used to perform database client calls. const ( // An identifier for the database management system (DBMS) product being used. See // below for a list of well-known identifiers. // // Type: Enum // Required: Always // Stability: stable DBSystemKey = attribute.Key("db.system") // The connection string used to connect to the database. It is recommended to // remove embedded credentials. // // Type: string // Required: No // Stability: stable // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' DBConnectionStringKey = attribute.Key("db.connection_string") // Username for accessing the database. // // Type: string // Required: No // Stability: stable // Examples: 'readonly_user', 'reporting_user' DBUserKey = attribute.Key("db.user") // The fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver // used to connect. // // Type: string // Required: No // Stability: stable // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") // If no [tech-specific attribute](#call-level-attributes-for-specific- // technologies) is defined, this attribute is used to report the name of the // database being accessed. For commands that switch the database, this should be // set to the target database (even if the command fails). // // Type: string // Required: Required, if applicable and no more-specific attribute is defined. // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called "schema // name". DBNameKey = attribute.Key("db.name") // The database statement being executed. // // Type: string // Required: Required if applicable and not explicitly disabled via // instrumentation configuration. // Stability: stable // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' // Note: The value may be sanitized to exclude sensitive information. DBStatementKey = attribute.Key("db.statement") // The name of the operation being executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. // // Type: string // Required: Required, if `db.statement` is not applicable. // Stability: stable // Examples: 'findAndModify', 'HMSET', 'SELECT' // Note: When setting this to an SQL keyword, it is not recommended to attempt any // client-side parsing of `db.statement` just to get this property, but it should // be set if the operation name is provided by the library being instrumented. If // the SQL statement has an ambiguous operation, or performs more than one // operation, this value may be omitted. DBOperationKey = attribute.Key("db.operation") ) var ( // Some other SQL database. Fallback only. See notes DBSystemOtherSQL = DBSystemKey.String("other_sql") // Microsoft SQL Server DBSystemMSSQL = DBSystemKey.String("mssql") // MySQL DBSystemMySQL = DBSystemKey.String("mysql") // Oracle Database DBSystemOracle = DBSystemKey.String("oracle") // IBM DB2 DBSystemDB2 = DBSystemKey.String("db2") // PostgreSQL DBSystemPostgreSQL = DBSystemKey.String("postgresql") // Amazon Redshift DBSystemRedshift = DBSystemKey.String("redshift") // Apache Hive DBSystemHive = DBSystemKey.String("hive") // Cloudscape DBSystemCloudscape = DBSystemKey.String("cloudscape") // HyperSQL DataBase DBSystemHSQLDB = DBSystemKey.String("hsqldb") // Progress Database DBSystemProgress = DBSystemKey.String("progress") // SAP MaxDB DBSystemMaxDB = DBSystemKey.String("maxdb") // SAP HANA DBSystemHanaDB = DBSystemKey.String("hanadb") // Ingres DBSystemIngres = DBSystemKey.String("ingres") // FirstSQL DBSystemFirstSQL = DBSystemKey.String("firstsql") // EnterpriseDB DBSystemEDB = DBSystemKey.String("edb") // InterSystems Caché DBSystemCache = DBSystemKey.String("cache") // Adabas (Adaptable Database System) DBSystemAdabas = DBSystemKey.String("adabas") // Firebird DBSystemFirebird = DBSystemKey.String("firebird") // Apache Derby DBSystemDerby = DBSystemKey.String("derby") // FileMaker DBSystemFilemaker = DBSystemKey.String("filemaker") // Informix DBSystemInformix = DBSystemKey.String("informix") // InstantDB DBSystemInstantDB = DBSystemKey.String("instantdb") // InterBase DBSystemInterbase = DBSystemKey.String("interbase") // MariaDB DBSystemMariaDB = DBSystemKey.String("mariadb") // Netezza DBSystemNetezza = DBSystemKey.String("netezza") // Pervasive PSQL DBSystemPervasive = DBSystemKey.String("pervasive") // PointBase DBSystemPointbase = DBSystemKey.String("pointbase") // SQLite DBSystemSqlite = DBSystemKey.String("sqlite") // Sybase DBSystemSybase = DBSystemKey.String("sybase") // Teradata DBSystemTeradata = DBSystemKey.String("teradata") // Vertica DBSystemVertica = DBSystemKey.String("vertica") // H2 DBSystemH2 = DBSystemKey.String("h2") // ColdFusion IMQ DBSystemColdfusion = DBSystemKey.String("coldfusion") // Apache Cassandra DBSystemCassandra = DBSystemKey.String("cassandra") // Apache HBase DBSystemHBase = DBSystemKey.String("hbase") // MongoDB DBSystemMongoDB = DBSystemKey.String("mongodb") // Redis DBSystemRedis = DBSystemKey.String("redis") // Couchbase DBSystemCouchbase = DBSystemKey.String("couchbase") // CouchDB DBSystemCouchDB = DBSystemKey.String("couchdb") // Microsoft Azure Cosmos DB DBSystemCosmosDB = DBSystemKey.String("cosmosdb") // Amazon DynamoDB DBSystemDynamoDB = DBSystemKey.String("dynamodb") // Neo4j DBSystemNeo4j = DBSystemKey.String("neo4j") // Apache Geode DBSystemGeode = DBSystemKey.String("geode") // Elasticsearch DBSystemElasticsearch = DBSystemKey.String("elasticsearch") // Memcached DBSystemMemcached = DBSystemKey.String("memcached") // CockroachDB DBSystemCockroachdb = DBSystemKey.String("cockroachdb") ) // Connection-level attributes for Microsoft SQL Server const ( // The Microsoft SQL Server [instance name](https://docs.microsoft.com/en- // us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named instance. // // Type: string // Required: No // Stability: stable // Examples: 'MSSQLSERVER' // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer // required (but still recommended if non-standard). DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") ) // Call-level attributes for Cassandra const ( // The name of the keyspace being accessed. To be used instead of the generic // `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'mykeyspace' DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") // The fetch size used for paging, i.e. how many rows will be returned at once. // // Type: int // Required: No // Stability: stable // Examples: 5000 DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") // The consistency level of the query. Based on consistency values from // [CQL](https://docs.datastax.com/en/cassandra- // oss/3.0/cassandra/dml/dmlConfigConsistency.html). // // Type: Enum // Required: No // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'mytable' // Note: This mirrors the db.sql.table attribute but references cassandra rather // than sql. It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBCassandraTableKey = attribute.Key("db.cassandra.table") // Whether or not the query is idempotent. // // Type: boolean // Required: No // Stability: stable DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") // The number of times a query was speculatively executed. Not set or `0` if the // query was not executed speculatively. // // Type: int // Required: No // Stability: stable // Examples: 0, 2 DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") // The ID of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") // The data center of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'us-west-2' DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") ) var ( // all DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") // each_quorum DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") // quorum DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") // local_quorum DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") // one DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") // two DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") // three DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") // local_one DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") // any DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") // serial DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") // local_serial DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) // Call-level attributes for Apache HBase const ( // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being // accessed. To be used instead of the generic `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'default' DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") ) // Call-level attributes for Redis const ( // The index of the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To be used // instead of the generic `db.name` attribute. // // Type: int // Required: Required, if other than the default database (`0`). // Stability: stable // Examples: 0, 1, 15 DBRedisDBIndexKey = attribute.Key("db.redis.database_index") ) // Call-level attributes for MongoDB const ( // The collection being accessed within the database stated in `db.name`. // // Type: string // Required: Always // Stability: stable // Examples: 'customers', 'products' DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) // Call-level attrbiutes for SQL databases const ( // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'public.users', 'customers' // Note: It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBSQLTableKey = attribute.Key("db.sql.table") ) // This document defines the attributes used to report a single exception associated with a span. const ( // The type of the exception (its fully-qualified class name, if applicable). The // dynamic type of the exception should be preferred over the static type in // languages that support it. // // Type: string // Required: No // Stability: stable // Examples: 'java.net.ConnectException', 'OSError' ExceptionTypeKey = attribute.Key("exception.type") // The exception message. // // Type: string // Required: No // Stability: stable // Examples: 'Division by zero', "Can't convert 'int' object to str implicitly" ExceptionMessageKey = attribute.Key("exception.message") // A stacktrace as a string in the natural representation for the language // runtime. The representation is to be determined and documented by each language // SIG. // // Type: string // Required: No // Stability: stable // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test // exception\\n at ' // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' ExceptionStacktraceKey = attribute.Key("exception.stacktrace") // SHOULD be set to true if the exception event is recorded at a point where it is // known that the exception is escaping the scope of the span. // // Type: boolean // Required: No // Stability: stable // Note: An exception is considered to have escaped (or left) the scope of a span, // if that span is ended while the exception is still logically "in flight". // This may be actually "in flight" in some languages (e.g. if the exception // is passed to a Context manager's `__exit__` method in Python) but will // usually be caught at the point of recording the exception in most languages. // It is usually not possible to determine at the point where an exception is // thrown // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending the span, // as done in the [example above](#exception-end-example). // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, // since the event might have been recorded at a time where it was not // clear whether the exception will escape. ExceptionEscapedKey = attribute.Key("exception.escaped") ) // This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. const ( // Type of the trigger on which the function is executed. // // Type: Enum // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing // invocations, if it is known to the client. This is, for example, not the case, // when the transport layer is abstracted in a FaaS client framework without // access to its configuration. // Stability: stable FaaSTriggerKey = attribute.Key("faas.trigger") // The execution ID of the current function execution. // // Type: string // Required: No // Stability: stable // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' FaaSExecutionKey = attribute.Key("faas.execution") ) var ( // A response to some data source operation such as a database or filesystem read/write FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") // To provide an answer to an inbound HTTP request FaaSTriggerHTTP = FaaSTriggerKey.String("http") // A function is set to be executed when messages are sent to a messaging system FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") // A function is scheduled to be executed regularly FaaSTriggerTimer = FaaSTriggerKey.String("timer") // If none of the others apply FaaSTriggerOther = FaaSTriggerKey.String("other") ) // Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. const ( // The name of the source on which the triggering operation was performed. For // example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos // DB to the database name. // // Type: string // Required: Always // Stability: stable // Examples: 'myBucketName', 'myDBName' FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") // Describes the type of the operation that was performed on the data. // // Type: Enum // Required: Always // Stability: stable FaaSDocumentOperationKey = attribute.Key("faas.document.operation") // A string containing the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSDocumentTimeKey = attribute.Key("faas.document.time") // The document name/table subjected to the operation. For example, in Cloud // Storage or S3 is the name of the file, and in Cosmos DB the table name. // // Type: string // Required: No // Stability: stable // Examples: 'myFile.txt', 'myTableName' FaaSDocumentNameKey = attribute.Key("faas.document.name") ) var ( // When a new object is created FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") // When an object is modified FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") // When an object is deleted FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") ) // Semantic Convention for FaaS scheduled to be executed regularly. const ( // A string containing the function invocation time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSTimeKey = attribute.Key("faas.time") // A string containing the schedule period as [Cron Expression](https://docs.oracl // e.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). // // Type: string // Required: No // Stability: stable // Examples: '0/5 * * * ? *' FaaSCronKey = attribute.Key("faas.cron") ) // Contains additional attributes for incoming FaaS spans. const ( // A boolean that is true if the serverless function is executed for the first // time (aka cold-start). // // Type: boolean // Required: No // Stability: stable FaaSColdstartKey = attribute.Key("faas.coldstart") ) // Contains additional attributes for outgoing FaaS spans. const ( // The name of the invoked function. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked // function. FaaSInvokedNameKey = attribute.Key("faas.invoked_name") // The cloud provider of the invoked function. // // Type: Enum // Required: Always // Stability: stable // Examples: 'aws' // Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked // function. FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") // The cloud region of the invoked function. // // Type: string // Required: For some cloud providers, like AWS or GCP, the region in which a // function is hosted is essential to uniquely identify the function and also part // of its endpoint. Since it's part of the endpoint being called, the region is // always known to clients. In these cases, `faas.invoked_region` MUST be set // accordingly. If the region is unknown to the client or not required for // identifying the invoked function, setting `faas.invoked_region` is optional. // Stability: stable // Examples: 'eu-central-1' // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked // function. FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") ) var ( // Amazon Web Services FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") // Microsoft Azure FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") ) // These attributes may be used for any network related operation. const ( // Transport protocol used. See note below. // // Type: Enum // Required: No // Stability: stable // Examples: 'ip_tcp' NetTransportKey = attribute.Key("net.transport") // Remote address of the peer (dotted decimal for IPv4 or // [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6) // // Type: string // Required: No // Stability: stable // Examples: '127.0.0.1' NetPeerIPKey = attribute.Key("net.peer.ip") // Remote port number. // // Type: int // Required: No // Stability: stable // Examples: 80, 8080, 443 NetPeerPortKey = attribute.Key("net.peer.port") // Remote hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'example.com' NetPeerNameKey = attribute.Key("net.peer.name") // Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. // // Type: string // Required: No // Stability: stable // Examples: '192.168.0.1' NetHostIPKey = attribute.Key("net.host.ip") // Like `net.peer.port` but for the host port. // // Type: int // Required: No // Stability: stable // Examples: 35555 NetHostPortKey = attribute.Key("net.host.port") // Local hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'localhost' NetHostNameKey = attribute.Key("net.host.name") ) var ( // ip_tcp NetTransportTCP = NetTransportKey.String("ip_tcp") // ip_udp NetTransportUDP = NetTransportKey.String("ip_udp") // Another IP-based protocol NetTransportIP = NetTransportKey.String("ip") // Unix Domain socket. See below NetTransportUnix = NetTransportKey.String("unix") // Named or anonymous pipe. See note below NetTransportPipe = NetTransportKey.String("pipe") // In-process communication NetTransportInProc = NetTransportKey.String("inproc") // Something else (non IP-based) NetTransportOther = NetTransportKey.String("other") ) // Operations that access some remote service. const ( // The [`service.name`](../../resource/semantic_conventions/README.md#service) of // the remote service. SHOULD be equal to the actual `service.name` resource // attribute of the remote service if any. // // Type: string // Required: No // Stability: stable // Examples: 'AuthTokenCache' PeerServiceKey = attribute.Key("peer.service") ) // These attributes may be used for any operation with an authenticated and/or authorized enduser. const ( // Username or client_id extracted from the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the // inbound request from outside the system. // // Type: string // Required: No // Stability: stable // Examples: 'username' EnduserIDKey = attribute.Key("enduser.id") // Actual/assumed role the client is making the request under extracted from token // or application security context. // // Type: string // Required: No // Stability: stable // Examples: 'admin' EnduserRoleKey = attribute.Key("enduser.role") // Scopes or granted authorities the client currently possesses extracted from // token or application security context. The value would come from the scope // associated with an [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value // in a [SAML 2.0 Assertion](http://docs.oasis- // open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). // // Type: string // Required: No // Stability: stable // Examples: 'read:message, write:files' EnduserScopeKey = attribute.Key("enduser.scope") ) // These attributes may be used for any operation to store information about a thread that started a span. const ( // Current "managed" thread ID (as opposed to OS thread ID). // // Type: int // Required: No // Stability: stable // Examples: 42 ThreadIDKey = attribute.Key("thread.id") // Current thread name. // // Type: string // Required: No // Stability: stable // Examples: 'main' ThreadNameKey = attribute.Key("thread.name") ) // These attributes allow to report this unit of code and therefore to provide more context about the span. const ( // The method or function name, or equivalent (usually rightmost part of the code // unit's name). // // Type: string // Required: No // Stability: stable // Examples: 'serveRequest' CodeFunctionKey = attribute.Key("code.function") // The "namespace" within which `code.function` is defined. Usually the qualified // class or module name, such that `code.namespace` + some separator + // `code.function` form a unique identifier for the code unit. // // Type: string // Required: No // Stability: stable // Examples: 'com.example.MyHTTPService' CodeNamespaceKey = attribute.Key("code.namespace") // The source code file name that identifies the code unit as uniquely as possible // (preferably an absolute file path). // // Type: string // Required: No // Stability: stable // Examples: '/usr/local/MyApplication/content_root/app/index.php' CodeFilepathKey = attribute.Key("code.filepath") // The line number in `code.filepath` best representing the operation. It SHOULD // point within the code unit named in `code.function`. // // Type: int // Required: No // Stability: stable // Examples: 42 CodeLineNumberKey = attribute.Key("code.lineno") ) // This document defines semantic conventions for HTTP client and server Spans. const ( // HTTP request method. // // Type: string // Required: Always // Stability: stable // Examples: 'GET', 'POST', 'HEAD' HTTPMethodKey = attribute.Key("http.method") // Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. // Usually the fragment is not transmitted over HTTP, but if it is known, it // should be included nevertheless. // // Type: string // Required: No // Stability: stable // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' // Note: `http.url` MUST NOT contain credentials passed via URL in form of // `https://username:password@www.example.com/`. In such case the attribute's // value should be `https://www.example.com/`. HTTPURLKey = attribute.Key("http.url") // The full request target as passed in a HTTP request line or equivalent. // // Type: string // Required: No // Stability: stable // Examples: '/path/12314/?q=ddds#123' HTTPTargetKey = attribute.Key("http.target") // The value of the [HTTP host // header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is // empty or not present, this attribute should be the same. // // Type: string // Required: No // Stability: stable // Examples: 'www.example.org' HTTPHostKey = attribute.Key("http.host") // The URI scheme identifying the used protocol. // // Type: string // Required: No // Stability: stable // Examples: 'http', 'https' HTTPSchemeKey = attribute.Key("http.scheme") // [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). // // Type: int // Required: If and only if one was received/sent. // Stability: stable // Examples: 200 HTTPStatusCodeKey = attribute.Key("http.status_code") // Kind of HTTP protocol used. // // Type: Enum // Required: No // Stability: stable // Examples: '1.0' // Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` // except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. HTTPFlavorKey = attribute.Key("http.flavor") // Value of the [HTTP User- // Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the // client. // // Type: string // Required: No // Stability: stable // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' HTTPUserAgentKey = attribute.Key("http.user_agent") // The size of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") // The size of the uncompressed request payload body after transport decoding. Not // set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPRequestContentLengthUncompressedKey = attribute.Key("http.request_content_length_uncompressed") // The size of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") // The size of the uncompressed response payload body after transport decoding. // Not set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") ) var ( // HTTP 1.0 HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") // HTTP 1.1 HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") // HTTP 2 HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") // SPDY protocol HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") // QUIC protocol HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") ) // Semantic Convention for HTTP Server const ( // The primary server name of the matched virtual host. This should be obtained // via configuration. If no such configuration can be obtained, this attribute // MUST NOT be set ( `net.host.name` should be used instead). // // Type: string // Required: No // Stability: stable // Examples: 'example.com' // Note: `http.url` is usually not readily available on the server side but would // have to be assembled in a cumbersome and sometimes lossy process from other // information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus // preferred to supply the raw data that is available. HTTPServerNameKey = attribute.Key("http.server_name") // The matched route (path template). // // Type: string // Required: No // Stability: stable // Examples: '/users/:userID?' HTTPRouteKey = attribute.Key("http.route") // The IP address of the original client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en- // US/docs/Web/HTTP/Headers/X-Forwarded-For)). // // Type: string // Required: No // Stability: stable // Examples: '83.164.160.102' // Note: This is not necessarily the same as `net.peer.ip`, which would identify // the network-level peer, which may be a proxy. HTTPClientIPKey = attribute.Key("http.client_ip") ) // Attributes that exist for multiple DynamoDB request types. const ( // The keys in the `RequestItems` object field. // // Type: string[] // Required: No // Stability: stable // Examples: 'Users', 'Cats' AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") // The JSON-serialized value of each item in the `ConsumedCapacity` response // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : { // "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": // "string", "WriteCapacityUnits": number }' AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") // The JSON-serialized value of the `ItemCollectionMetrics` response field. // // Type: string // Required: No // Stability: stable // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }' AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") // The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") // The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") // The value of the `ConsistentRead` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") // The value of the `ProjectionExpression` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Title', 'Title, Price, Color', 'Title, Description, RelatedItems, // ProductReviews' AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") // The value of the `Limit` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") // The value of the `AttributesToGet` request parameter. // // Type: string[] // Required: No // Stability: stable // Examples: 'lives', 'id' AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") // The value of the `IndexName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'name_to_group' AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") // The value of the `Select` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'ALL_ATTRIBUTES', 'COUNT' AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") ) // DynamoDB.CreateTable const ( // The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request // field // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" }, "ProvisionedThroughput": { "ReadCapacityUnits": // number, "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") // The JSON-serialized value of each item of the `LocalSecondaryIndexes` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexARN": "string", "IndexName": "string", "IndexSizeBytes": // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" } }' AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") ) // DynamoDB.ListTables const ( // The value of the `ExclusiveStartTableName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Users', 'CatsTable' AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") // The the number of items in the `TableNames` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 20 AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") ) // DynamoDB.Query const ( // The value of the `ScanIndexForward` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") ) // DynamoDB.Scan const ( // The value of the `Segment` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") // The value of the `TotalSegments` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 100 AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") // The value of the `Count` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") // The value of the `ScannedCount` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 50 AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") ) // DynamoDB.UpdateTable const ( // The JSON-serialized value of each item in the `AttributeDefinitions` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") // The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` // request field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }' AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") ) // This document defines the attributes used in messaging systems. const ( // A string identifying the messaging system. // // Type: string // Required: Always // Stability: stable // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // The message destination name. This might be equal to the span name but is // required nevertheless. // // Type: string // Required: Always // Stability: stable // Examples: 'MyQueue', 'MyTopic' MessagingDestinationKey = attribute.Key("messaging.destination") // The kind of message destination // // Type: Enum // Required: Required only if the message destination is either a `queue` or // `topic`. // Stability: stable MessagingDestinationKindKey = attribute.Key("messaging.destination_kind") // A boolean that is true if the message destination is temporary. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingTempDestinationKey = attribute.Key("messaging.temp_destination") // The name of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: 'AMQP', 'MQTT' MessagingProtocolKey = attribute.Key("messaging.protocol") // The version of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: '0.9.1' MessagingProtocolVersionKey = attribute.Key("messaging.protocol_version") // Connection string. // // Type: string // Required: No // Stability: stable // Examples: 'tibjmsnaming://localhost:7222', // 'https://queue.amazonaws.com/80398EXAMPLE/MyQueue' MessagingURLKey = attribute.Key("messaging.url") // A value used by the messaging system as an identifier for the message, // represented as a string. // // Type: string // Required: No // Stability: stable // Examples: '452a7c7c7c7048c2f887f61572b18fc2' MessagingMessageIDKey = attribute.Key("messaging.message_id") // The [conversation ID](#conversations) identifying the conversation to which the // message belongs, represented as a string. Sometimes called "Correlation ID". // // Type: string // Required: No // Stability: stable // Examples: 'MyConversationID' MessagingConversationIDKey = attribute.Key("messaging.conversation_id") // The (uncompressed) size of the message payload in bytes. Also use this // attribute if it is unknown whether the compressed or uncompressed payload size // is reported. // // Type: int // Required: No // Stability: stable // Examples: 2738 MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message_payload_size_bytes") // The compressed size of the message payload in bytes. // // Type: int // Required: No // Stability: stable // Examples: 2048 MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message_payload_compressed_size_bytes") ) var ( // A message sent to a queue MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") // A message sent to a topic MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") ) // Semantic convention for a consumer of messages received from a messaging system const ( // A string identifying the kind of message consumption as defined in the // [Operation names](#operation-names) section above. If the operation is "send", // this attribute MUST NOT be set, since the operation can be inferred from the // span kind in that case. // // Type: Enum // Required: No // Stability: stable MessagingOperationKey = attribute.Key("messaging.operation") ) var ( // receive MessagingOperationReceive = MessagingOperationKey.String("receive") // process MessagingOperationProcess = MessagingOperationKey.String("process") ) // Attributes for RabbitMQ const ( // RabbitMQ message routing key. // // Type: string // Required: Unless it is empty. // Stability: stable // Examples: 'myKey' MessagingRabbitmqRoutingKeyKey = attribute.Key("messaging.rabbitmq.routing_key") ) // Attributes for Apache Kafka const ( // Message keys in Kafka are used for grouping alike messages to ensure they're // processed on the same partition. They differ from `messaging.message_id` in // that they're not unique. If the key is `null`, the attribute MUST NOT be set. // // Type: string // Required: No // Stability: stable // Examples: 'myKey' // Note: If the key type is not string, it's string representation has to be // supplied for the attribute. If the key has no unambiguous, canonical string // form, don't include its value. MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message_key") // Name of the Kafka Consumer Group that is handling the message. Only applies to // consumers, not producers. // // Type: string // Required: No // Stability: stable // Examples: 'my-group' MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer_group") // Client ID for the Consumer or Producer that is handling the message. // // Type: string // Required: No // Stability: stable // Examples: 'client-5' MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") // Partition the message is sent to. // // Type: int // Required: No // Stability: stable // Examples: 2 MessagingKafkaPartitionKey = attribute.Key("messaging.kafka.partition") // A boolean that is true if the message is a tombstone. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") ) // This document defines semantic conventions for remote procedure calls. const ( // A string identifying the remoting system. // // Type: string // Required: Always // Stability: stable // Examples: 'grpc', 'java_rmi', 'wcf' RPCSystemKey = attribute.Key("rpc.system") // The full name of the service being called, including its package name, if // applicable. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'myservice.EchoService' RPCServiceKey = attribute.Key("rpc.service") // The name of the method being called, must be equal to the $method part in the // span name. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'exampleMethod' RPCMethodKey = attribute.Key("rpc.method") ) // Tech-specific attributes for gRPC. const ( // The [numeric status // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC // request. // // Type: Enum // Required: Always // Stability: stable // Examples: 0, 1, 16 RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) var ( // OK RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) // CANCELLED RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) // UNKNOWN RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) // INVALID_ARGUMENT RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) // DEADLINE_EXCEEDED RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) // NOT_FOUND RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) // ALREADY_EXISTS RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) // PERMISSION_DENIED RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) // RESOURCE_EXHAUSTED RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) // FAILED_PRECONDITION RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) // ABORTED RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) // OUT_OF_RANGE RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) // UNIMPLEMENTED RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) // INTERNAL RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) // UNAVAILABLE RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) // DATA_LOSS RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) // UNAUTHENTICATED RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) ) // Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). const ( // Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC // 1.0 does not specify this, the value can be omitted. // // Type: string // Required: If missing, it is assumed to be "1.0". // Stability: stable // Examples: '2.0', '1.0' RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") // `method` property from request. Unlike `rpc.method`, this may not relate to the // actual method being called. Useful for client-side traces since client does not // know what will be called on the server. // // Type: string // Required: Always // Stability: stable // Examples: 'users.create', 'get_users' RPCJsonrpcMethodKey = attribute.Key("rpc.jsonrpc.method") // `id` property of request or response. Since protocol allows id to be int, // string, `null` or missing (for notifications), value is expected to be cast to // string for simplicity. Use empty string in case of `null` value. Omit entirely // if this is a notification. // // Type: string // Required: No // Stability: stable // Examples: '10', 'request-7', '' RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") // `error.code` property of response if it is an error response. // // Type: int // Required: If missing, response is assumed to be successful. // Stability: stable // Examples: -32700, 100 RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") // `error.message` property of response if it is an error response. // // Type: string // Required: No // Stability: stable // Examples: 'Parse error', 'User already exists' RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") ) golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/000077500000000000000000000000001414226744000211205ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/doc.go000066400000000000000000000016621414226744000222210ustar00rootroot00000000000000// 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 semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions // as of the v1.5.0 version of the OpenTelemetry specification. package semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/exception.go000066400000000000000000000014271414226744000234510ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" const ( // ExceptionEventName is the name of the Span event representing an exception. ExceptionEventName = "exception" ) golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/http.go000066400000000000000000000217301414226744000224310ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" import ( "fmt" "net" "net/http" "strconv" "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // HTTP scheme attributes. var ( HTTPSchemeHTTP = HTTPSchemeKey.String("http") HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} switch network { case "tcp", "tcp4", "tcp6": attrs = append(attrs, NetTransportTCP) case "udp", "udp4", "udp6": attrs = append(attrs, NetTransportUDP) case "ip", "ip4", "ip6": attrs = append(attrs, NetTransportIP) case "unix", "unixgram", "unixpacket": attrs = append(attrs, NetTransportUnix) default: attrs = append(attrs, NetTransportOther) } peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) if peerIP != "" { attrs = append(attrs, NetPeerIPKey.String(peerIP)) } if peerName != "" { attrs = append(attrs, NetPeerNameKey.String(peerName)) } if peerPort != 0 { attrs = append(attrs, NetPeerPortKey.Int(peerPort)) } hostIP, hostName, hostPort := "", "", 0 for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { hostIP, hostName, hostPort = hostIPNamePort(someHost) if hostIP != "" || hostName != "" || hostPort != 0 { break } } if hostIP != "" { attrs = append(attrs, NetHostIPKey.String(hostIP)) } if hostName != "" { attrs = append(attrs, NetHostNameKey.String(hostName)) } if hostPort != 0 { attrs = append(attrs, NetHostPortKey.Int(hostPort)) } return attrs } // hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. // It handles both IPv4 and IPv6 addresses. If the host portion is not recognized // as a valid IPv4 or IPv6 address, the `ip` result will be empty and the // host portion will instead be returned in `name`. func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { var ( hostPart, portPart string parsedPort uint64 err error ) if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { hostPart, portPart = hostWithPort, "" } if parsedIP := net.ParseIP(hostPart); parsedIP != nil { ip = parsedIP.String() } else { name = hostPart } if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { port = int(parsedPort) } return } // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { if username, _, ok := request.BasicAuth(); ok { return []attribute.KeyValue{EnduserIDKey.String(username)} } return nil } // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if request.Method != "" { attrs = append(attrs, HTTPMethodKey.String(request.Method)) } else { attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) } // remove any username/password info that may be in the URL // before adding it to the attributes userinfo := request.URL.User request.URL.User = nil attrs = append(attrs, HTTPURLKey.String(request.URL.String())) // restore any username/password info that was removed request.URL.User = userinfo return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if ua := request.UserAgent(); ua != "" { attrs = append(attrs, HTTPUserAgentKey.String(ua)) } if request.ContentLength > 0 { attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality attrs := []attribute.KeyValue{} if request.TLS != nil { attrs = append(attrs, HTTPSchemeHTTPS) } else { attrs = append(attrs, HTTPSchemeHTTP) } if request.Host != "" { attrs = append(attrs, HTTPHostKey.String(request.Host)) } flavor := "" if request.ProtoMajor == 1 { flavor = fmt.Sprintf("1.%d", request.ProtoMinor) } else if request.ProtoMajor == 2 { flavor = "2" } if flavor != "" { attrs = append(attrs, HTTPFlavorKey.String(flavor)) } return attrs } // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } // HTTPServerAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the server side. Currently, only basic authentication is // supported. func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPMethodKey.String(request.Method), HTTPTargetKey.String(request.RequestURI), } if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } if route != "" { attrs = append(attrs, HTTPRouteKey.String(route)) } if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) } } return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPStatusCodeKey.Int(code), } return attrs } type codeRange struct { fromInclusive int toInclusive int } func (r codeRange) contains(code int) bool { return r.fromInclusive <= code && code <= r.toInclusive } var validRangesPerCategory = map[int][]codeRange{ 1: { {http.StatusContinue, http.StatusEarlyHints}, }, 2: { {http.StatusOK, http.StatusAlreadyReported}, {http.StatusIMUsed, http.StatusIMUsed}, }, 3: { {http.StatusMultipleChoices, http.StatusUseProxy}, {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, }, 4: { {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, {http.StatusPreconditionRequired, http.StatusTooManyRequests}, {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, }, 5: { {http.StatusInternalServerError, http.StatusLoopDetected}, {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, }, } // SpanStatusFromHTTPStatusCode generates a status code and a message // as specified by the OpenTelemetry specification for a span. func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { spanCode, valid := validateHTTPStatusCode(code) if !valid { return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) } return spanCode, "" } // Validates the HTTP status code and returns corresponding span status code. // If the `code` is not a valid HTTP status code, returns span status Error // and false. func validateHTTPStatusCode(code int) (codes.Code, bool) { category := code / 100 ranges, ok := validRangesPerCategory[category] if !ok { return codes.Error, false } ok = false for _, crange := range ranges { ok = crange.contains(code) if ok { break } } if !ok { return codes.Error, false } if category > 0 && category < 4 { return codes.Unset, true } return codes.Error, true } golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/http_test.go000066400000000000000000000725421414226744000234770ustar00rootroot00000000000000// 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 semconv import ( "crypto/tls" "net/http" "net/url" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) type tlsOption int const ( noTLS tlsOption = iota withTLS ) func TestNetAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string network string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped, tcp", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), }, }, { name: "stripped, udp", network: "udp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_udp"), }, }, { name: "stripped, ip", network: "ip", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip"), }, }, { name: "stripped, unix", network: "unix", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "unix"), }, }, { name: "stripped, other", network: "nih", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "other"), }, }, { name: "with remote ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[fe80::0202:b3ff:fe1e:8329]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4-in-v6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[::ffff:192.168.0.1]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), }, }, { name: "with remote ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "fe80::0202:b3ff:fe1e:8329", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with remote ipv4_in_v6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "::ffff:192.168.0.1", // section 2.5.5.2 of RFC4291 host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), }, }, { name: "with remote name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), }, }, { name: "with remote port only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: ":56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.Int("net.peer.port", 56), }, }, { name: "with host name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "fe80::0202:b3ff:fe1e:8329", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with host name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, { name: "with host name and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with empty host and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: ":80", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.Int("net.host.port", 80), }, }, { name: "with host ip and port in headers", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "Host": []string{"4.3.2.1:78"}, }, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "4.3.2.1:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "[fe80::202:b3ff:fe1e:8329]:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS) got := NetAttributesFromHTTPRequest(tc.network, r) if diff := cmp.Diff( tc.expected, got, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("attributes differ: diff %+v,", diff) } }) } } func TestEndUserAttributesFromHTTPRequest(t *testing.T) { r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS) var expected []attribute.KeyValue got := EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) r.SetBasicAuth("admin", "password") expected = []attribute.KeyValue{attribute.String("enduser.id", "admin")} got = EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) } func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string serverName string route string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped", serverName: "", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with server name", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with tls", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with route", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), }, }, { name: "with host", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with proxy info", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"203.0.113.195, 70.41.3.18, 150.172.238.178"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "203.0.113.195"), }, }, { name: "with http 1.1", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with http 2", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with content length", method: "GET", requestURI: "/user/123", contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, } for idx, tc := range testcases { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r) assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) } } func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) { expected := []attribute.KeyValue{ attribute.Int("http.status_code", 404), } got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound) assertElementsMatch(t, expected, got, "with valid HTTP status code") assert.ElementsMatch(t, expected, got) expected = []attribute.KeyValue{ attribute.Int("http.status_code", 499), } got = HTTPAttributesFromHTTPStatusCode(499) assertElementsMatch(t, expected, got, "with invalid HTTP status code") } func TestSpanStatusFromHTTPStatusCode(t *testing.T) { for code := 0; code < 1000; code++ { expected := getExpectedCodeForHTTPCode(code) got, msg := SpanStatusFromHTTPStatusCode(code) assert.Equalf(t, expected, got, "%s vs %s", expected, got) _, valid := validateHTTPStatusCode(code) if !valid { assert.NotEmpty(t, msg, "message should be set if error cannot be inferred from code") } else { assert.Empty(t, msg, "message should not be set if error can be inferred from code") } } } func getExpectedCodeForHTTPCode(code int) codes.Code { if http.StatusText(code) == "" { return codes.Error } switch code { case http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusTooManyRequests, http.StatusNotImplemented, http.StatusServiceUnavailable, http.StatusGatewayTimeout: return codes.Error } category := code / 100 if category > 0 && category < 4 { return codes.Unset } return codes.Error } func assertElementsMatch(t *testing.T, expected, got []attribute.KeyValue, format string, args ...interface{}) { if !assert.ElementsMatchf(t, expected, got, format, args...) { t.Log("expected:", kvStr(expected)) t.Log("got:", kvStr(got)) } } func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request { major, minor := protoToInts(proto) var tlsConn *tls.ConnectionState switch tlsopt { case noTLS: case withTLS: tlsConn = &tls.ConnectionState{} } return &http.Request{ Method: method, URL: u, Proto: proto, ProtoMajor: major, ProtoMinor: minor, Header: header, Host: host, RemoteAddr: remoteAddr, RequestURI: requestURI, TLS: tlsConn, } } func protoToInts(proto string) (int, int) { switch proto { case "HTTP/1.0": return 1, 0 case "HTTP/1.1": return 1, 1 case "HTTP/2.0": return 2, 0 } // invalid proto return 13, 42 } func kvStr(kvs []attribute.KeyValue) string { sb := strings.Builder{} sb.WriteRune('[') for idx, label := range kvs { if idx > 0 { sb.WriteString(", ") } sb.WriteString((string)(label.Key)) sb.WriteString(": ") sb.WriteString(label.Value.Emit()) } sb.WriteRune(']') return sb.String() } func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) { testCases := []struct { name string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue }{ { name: "stripped", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with tls", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), }, }, { name: "with host", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 1.1", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 2", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with content length", method: "GET", url: &url.URL{ Path: "/user/123", }, contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, { name: "with empty method (fallback to GET)", method: "", url: &url.URL{ Path: "/user/123", }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, { name: "authentication information is stripped", method: "", url: &url.URL{ Path: "/user/123", User: url.UserPassword("foo", "bar"), }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPClientAttributesFromHTTPRequest(r) assert.ElementsMatch(t, tc.expected, got) }) } } golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/resource.go000066400000000000000000000753061414226744000233110ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" import "go.opentelemetry.io/otel/attribute" // A cloud environment (e.g. GCP, Azure, AWS) const ( // Name of the cloud provider. // // Type: Enum // Required: No // Stability: stable CloudProviderKey = attribute.Key("cloud.provider") // The cloud account ID the resource is assigned to. // // Type: string // Required: No // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") // The geographical region the resource is running. Refer to your provider's docs // to see the available regions, for example [AWS // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), // [Azure regions](https://azure.microsoft.com/en-us/global- // infrastructure/geographies/), or [Google Cloud // regions](https://cloud.google.com/about/locations). // // Type: string // Required: No // Stability: stable // Examples: 'us-central1', 'us-east-1' CloudRegionKey = attribute.Key("cloud.region") // Cloud regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the resource // is running. // // Type: string // Required: No // Stability: stable // Examples: 'us-east-1c' // Note: Availability zones are called "zones" on Google Cloud. CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") // The cloud platform in use. // // Type: Enum // Required: No // Stability: stable // Note: The prefix of the service SHOULD match the one specified in // `cloud.provider`. CloudPlatformKey = attribute.Key("cloud.platform") ) var ( // Amazon Web Services CloudProviderAWS = CloudProviderKey.String("aws") // Microsoft Azure CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") ) var ( // AWS Elastic Compute Cloud CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") // AWS Elastic Container Service CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") // AWS Elastic Kubernetes Service CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") // AWS Lambda CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") // Azure Kubernetes Service CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") // Azure Functions CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") // Azure App Service CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") // Google Cloud Compute Engine (GCE) CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") // Google Cloud Run CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") // Google Cloud Kubernetes Engine (GKE) CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") // Google Cloud Functions (GCF) CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") ) // Resources used by AWS Elastic Container Service (ECS). const ( // The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws. // amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") // The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo // perguide/clusters.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") // The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l // aunch_types.html) for an ECS task. // // Type: Enum // Required: No // Stability: stable AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") // The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates // t/developerguide/task_definitions.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") // The task definition family this task definition is a member of. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-family' AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") // The revision for this task definition. // // Type: string // Required: No // Stability: stable // Examples: '8', '26' AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") ) var ( // ec2 AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") // fargate AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") ) // Resources used by AWS Elastic Kubernetes Service (EKS). const ( // The ARN of an EKS cluster. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") ) // Resources specific to Amazon Web Services. const ( // The name(s) of the AWS log group(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: '/aws/lambda/my-function', 'opentelemetry-service' // Note: Multiple log groups must be supported for cases like multi-container // applications, where a single application has sidecar containers, and each write // to their own log group. AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") // The Amazon Resource Name(s) (ARN) of the AWS log group(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' // Note: See the [log group ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") // The name(s) of the AWS log stream(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") // The ARN(s) of the AWS log stream(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log- // stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' // Note: See the [log stream ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain // several log streams, so these ARNs necessarily identify both a log group and a // log stream. AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") ) // A container instance. const ( // Container name. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-autoconf' ContainerNameKey = attribute.Key("container.name") // Container ID. Usually a UUID, as for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container- // identification). The UUID might be abbreviated. // // Type: string // Required: No // Stability: stable // Examples: 'a3bf90e006b2' ContainerIDKey = attribute.Key("container.id") // The container runtime managing this container. // // Type: string // Required: No // Stability: stable // Examples: 'docker', 'containerd', 'rkt' ContainerRuntimeKey = attribute.Key("container.runtime") // Name of the image the container was built on. // // Type: string // Required: No // Stability: stable // Examples: 'gcr.io/opentelemetry/operator' ContainerImageNameKey = attribute.Key("container.image.name") // Container image tag. // // Type: string // Required: No // Stability: stable // Examples: '0.1' ContainerImageTagKey = attribute.Key("container.image.tag") ) // The software deployment. const ( // Name of the [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). // // Type: string // Required: No // Stability: stable // Examples: 'staging', 'production' DeploymentEnvironmentKey = attribute.Key("deployment.environment") ) // The device on which the process represented by this resource is running. const ( // A unique identifier representing the device // // Type: string // Required: No // Stability: stable // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' // Note: The device identifier MUST only be defined using the values outlined // below. This value is not an advertising identifier and MUST NOT be used as // such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id // entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden // tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the // Firebase Installation ID or a globally unique UUID which is persisted across // sessions in your application. More information can be found // [here](https://developer.android.com/training/articles/user-data-ids) on best // practices and exact implementation details. Caution should be taken when // storing personal data or anything which can identify a user. GDPR and data // protection laws may apply, ensure you do your own due diligence. DeviceIDKey = attribute.Key("device.id") // The model identifier for the device // // Type: string // Required: No // Stability: stable // Examples: 'iPhone3,4', 'SM-G920F' // Note: It's recommended this value represents a machine readable version of the // model identifier rather than the market or consumer-friendly name of the // device. DeviceModelIdentifierKey = attribute.Key("device.model.identifier") // The marketing name for the device model // // Type: string // Required: No // Stability: stable // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' // Note: It's recommended this value represents a human readable version of the // device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") ) // A serverless instance. const ( // The name of the single function that this runtime instance executes. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: This is the name of the function as configured/deployed on the FaaS // platform and is usually different from the name of the callback function (which // may be stored in the // [`code.namespace`/`code.function`](../../trace/semantic_conventions/span- // general.md#source-code-attributes) span attributes). FaaSNameKey = attribute.Key("faas.name") // The unique ID of the single function that this runtime instance executes. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' // Note: Depending on the cloud provider, use: // * **AWS Lambda:** The function // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and- // namespaces.html). // Take care not to use the "invoked ARN" directly but replace any // [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // aliases.html) with the resolved function version, as the same runtime instance // may be invokable with multiple // different aliases. // * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full- // resource-names) // * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en- // us/rest/api/resources/resources/get-by-id). // On some providers, it may not be possible to determine the full ID at startup, // which is why this field cannot be made required. For example, on AWS the // account ID // part of the ARN is not available without calling another AWS API // which may be deemed too slow for a short-running lambda function. // As an alternative, consider setting `faas.id` as a span attribute instead. FaaSIDKey = attribute.Key("faas.id") // The immutable version of the function being executed. // // Type: string // Required: No // Stability: stable // Examples: '26', 'pinkfroid-00002' // Note: Depending on the cloud provider and platform, use: // * **AWS Lambda:** The [function // version](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // versions.html) // (an integer represented as a decimal string). // * **Google Cloud Run:** The // [revision](https://cloud.google.com/run/docs/managing/revisions) // (i.e., the function name plus the revision suffix). // * **Google Cloud Functions:** The value of the // [`K_REVISION` environment // variable](https://cloud.google.com/functions/docs/env- // var#runtime_environment_variables_set_automatically). // * **Azure Functions:** Not applicable. Do not set this attribute. FaaSVersionKey = attribute.Key("faas.version") // The execution environment ID as a string, that will be potentially reused for // other invocations to the same function/function version. // // Type: string // Required: No // Stability: stable // Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de' // Note: * **AWS Lambda:** Use the (full) log stream name. FaaSInstanceKey = attribute.Key("faas.instance") // The amount of memory available to the serverless function in MiB. // // Type: int // Required: No // Stability: stable // Examples: 128 // Note: It's recommended to set this attribute since e.g. too little memory can // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this // information. FaaSMaxMemoryKey = attribute.Key("faas.max_memory") ) // A host is defined as a general computing instance. const ( // Unique host ID. For Cloud, this must be the instance_id assigned by the cloud // provider. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostIDKey = attribute.Key("host.id") // Name of the host. On Unix systems, it may contain what the hostname command // returns, or the fully qualified hostname, or another name specified by the // user. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostNameKey = attribute.Key("host.name") // Type of host. For Cloud, this must be the machine type. // // Type: string // Required: No // Stability: stable // Examples: 'n1-standard-1' HostTypeKey = attribute.Key("host.type") // The CPU architecture the host system is running on. // // Type: Enum // Required: No // Stability: stable HostArchKey = attribute.Key("host.arch") // Name of the VM image or OS install the host was instantiated from. // // Type: string // Required: No // Stability: stable // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' HostImageNameKey = attribute.Key("host.image.name") // VM image ID. For Cloud, this value is from the provider. // // Type: string // Required: No // Stability: stable // Examples: 'ami-07b06b442921831e5' HostImageIDKey = attribute.Key("host.image.id") // The version string of the VM image as defined in [Version // Attributes](README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '0.1' HostImageVersionKey = attribute.Key("host.image.version") ) var ( // AMD64 HostArchAMD64 = HostArchKey.String("amd64") // ARM32 HostArchARM32 = HostArchKey.String("arm32") // ARM64 HostArchARM64 = HostArchKey.String("arm64") // Itanium HostArchIA64 = HostArchKey.String("ia64") // 32-bit PowerPC HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) // A Kubernetes Cluster. const ( // The name of the cluster. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-cluster' K8SClusterNameKey = attribute.Key("k8s.cluster.name") ) // A Kubernetes Node object. const ( // The name of the Node. // // Type: string // Required: No // Stability: stable // Examples: 'node-1' K8SNodeNameKey = attribute.Key("k8s.node.name") // The UID of the Node. // // Type: string // Required: No // Stability: stable // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' K8SNodeUIDKey = attribute.Key("k8s.node.uid") ) // A Kubernetes Namespace. const ( // The name of the namespace that the pod is running in. // // Type: string // Required: No // Stability: stable // Examples: 'default' K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") ) // A Kubernetes Pod object. const ( // The UID of the Pod. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SPodUIDKey = attribute.Key("k8s.pod.uid") // The name of the Pod. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-pod-autoconf' K8SPodNameKey = attribute.Key("k8s.pod.name") ) // A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( // The name of the Container in a Pod template. // // Type: string // Required: No // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") ) // A Kubernetes ReplicaSet object. const ( // The UID of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") // The name of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") ) // A Kubernetes Deployment object. const ( // The UID of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") // The name of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") ) // A Kubernetes StatefulSet object. const ( // The UID of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") // The name of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") ) // A Kubernetes DaemonSet object. const ( // The UID of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") // The name of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") ) // A Kubernetes Job object. const ( // The UID of the Job. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SJobUIDKey = attribute.Key("k8s.job.uid") // The name of the Job. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SJobNameKey = attribute.Key("k8s.job.name") ) // A Kubernetes CronJob object. const ( // The UID of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") // The name of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") ) // The operating system (OS) on which the process represented by this resource is running. const ( // The operating system type. // // Type: Enum // Required: Always // Stability: stable OSTypeKey = attribute.Key("os.type") // Human readable (not intended to be parsed) OS version information, like e.g. // reported by `ver` or `lsb_release -a` commands. // // Type: string // Required: No // Stability: stable // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS' OSDescriptionKey = attribute.Key("os.description") // Human readable operating system name. // // Type: string // Required: No // Stability: stable // Examples: 'iOS', 'Android', 'Ubuntu' OSNameKey = attribute.Key("os.name") // The version string of the operating system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '14.2.1', '18.04.1' OSVersionKey = attribute.Key("os.version") ) var ( // Microsoft Windows OSTypeWindows = OSTypeKey.String("windows") // Linux OSTypeLinux = OSTypeKey.String("linux") // Apple Darwin OSTypeDarwin = OSTypeKey.String("darwin") // FreeBSD OSTypeFreeBSD = OSTypeKey.String("freebsd") // NetBSD OSTypeNetBSD = OSTypeKey.String("netbsd") // OpenBSD OSTypeOpenBSD = OSTypeKey.String("openbsd") // DragonFly BSD OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") // HP-UX (Hewlett Packard Unix) OSTypeHPUX = OSTypeKey.String("hpux") // AIX (Advanced Interactive eXecutive) OSTypeAIX = OSTypeKey.String("aix") // Oracle Solaris OSTypeSolaris = OSTypeKey.String("solaris") // IBM z/OS OSTypeZOS = OSTypeKey.String("z_os") ) // An operating system process. const ( // Process identifier (PID). // // Type: int // Required: No // Stability: stable // Examples: 1234 ProcessPIDKey = attribute.Key("process.pid") // The name of the process executable. On Linux based systems, can be set to the // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: 'otelcol' ProcessExecutableNameKey = attribute.Key("process.executable.name") // The full path to the process executable. On Linux based systems, can be set to // the target of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: '/usr/bin/cmd/otelcol' ProcessExecutablePathKey = attribute.Key("process.executable.path") // The command used to launch the process (i.e. the command name). On Linux based // systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, // can be set to the first parameter extracted from `GetCommandLineW`. // // Type: string // Required: See below // Stability: stable // Examples: 'cmd/otelcol' ProcessCommandKey = attribute.Key("process.command") // The full command used to launch the process as a single string representing the // full command. On Windows, can be set to the result of `GetCommandLineW`. Do not // set this if you have to assemble it just for monitoring; use // `process.command_args` instead. // // Type: string // Required: See below // Stability: stable // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' ProcessCommandLineKey = attribute.Key("process.command_line") // All the command arguments (including the command/executable itself) as received // by the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited strings // extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be // the full argv vector passed to `main`. // // Type: string[] // Required: See below // Stability: stable // Examples: 'cmd/otecol', '--config=config.yaml' ProcessCommandArgsKey = attribute.Key("process.command_args") // The username of the user that owns the process. // // Type: string // Required: No // Stability: stable // Examples: 'root' ProcessOwnerKey = attribute.Key("process.owner") ) // The single (language) runtime instance which is monitored. const ( // The name of the runtime of this process. For compiled native binaries, this // SHOULD be the name of the compiler. // // Type: string // Required: No // Stability: stable // Examples: 'OpenJDK Runtime Environment' ProcessRuntimeNameKey = attribute.Key("process.runtime.name") // The version of the runtime of this process, as returned by the runtime without // modification. // // Type: string // Required: No // Stability: stable // Examples: '14.0.2' ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") // An additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. // // Type: string // Required: No // Stability: stable // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") ) // A service instance. const ( // Logical name of the service. // // Type: string // Required: Always // Stability: stable // Examples: 'shoppingcart' // Note: MUST be the same for all instances of horizontally scaled services. If // the value was not specified, SDKs MUST fallback to `unknown_service:` // concatenated with [`process.executable.name`](process.md#process), e.g. // `unknown_service:bash`. If `process.executable.name` is not available, the // value MUST be set to `unknown_service`. ServiceNameKey = attribute.Key("service.name") // A namespace for `service.name`. // // Type: string // Required: No // Stability: stable // Examples: 'Shop' // Note: A string value having a meaning that helps to distinguish a group of // services, for example the team name that owns a group of services. // `service.name` is expected to be unique within the same namespace. If // `service.namespace` is not specified in the Resource then `service.name` is // expected to be unique for all services that have no explicit namespace defined // (so the empty/unspecified namespace is simply one more valid namespace). Zero- // length namespace string is assumed equal to unspecified namespace. ServiceNamespaceKey = attribute.Key("service.namespace") // The string ID of the service instance. // // Type: string // Required: No // Stability: stable // Examples: '627cc493-f310-47de-96bd-71410b7dec09' // Note: MUST be unique for each instance of the same // `service.namespace,service.name` pair (in other words // `service.namespace,service.name,service.instance.id` triplet MUST be globally // unique). The ID helps to distinguish instances of the same service that exist // at the same time (e.g. instances of a horizontally scaled service). It is // preferable for the ID to be persistent and stay the same for the lifetime of // the service instance, however it is acceptable that the ID is ephemeral and // changes during important lifetime events for the service (e.g. service // restarts). If the service has no inherent unique ID that can be used as the // value of this attribute it is recommended to generate a random Version 1 or // Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use // Version 5, see RFC 4122 for more recommendations). ServiceInstanceIDKey = attribute.Key("service.instance.id") // The version string of the service API or implementation. // // Type: string // Required: No // Stability: stable // Examples: '2.0.0' ServiceVersionKey = attribute.Key("service.version") ) // The telemetry SDK used to capture data recorded by the instrumentation libraries. const ( // The name of the telemetry SDK as defined above. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") // The language of the telemetry SDK. // // Type: Enum // Required: No // Stability: stable TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") // The version string of the telemetry SDK. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") // The version string of the auto instrumentation agent, if used. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") ) var ( // cpp TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") // dotnet TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") // erlang TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") // go TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") // java TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") // nodejs TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") // php TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") // python TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") // ruby TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") ) // Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. const ( // The name of the web engine. // // Type: string // Required: Always // Stability: stable // Examples: 'WildFly' WebEngineNameKey = attribute.Key("webengine.name") // The version of the web engine. // // Type: string // Required: No // Stability: stable // Examples: '21.0.0' WebEngineVersionKey = attribute.Key("webengine.version") // Additional description of the web engine (e.g. detailed version and edition // information). // // Type: string // Required: No // Stability: stable // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final' WebEngineDescriptionKey = attribute.Key("webengine.description") ) golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/schema.go000066400000000000000000000017131414226744000227110ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ const SchemaURL = "https://opentelemetry.io/schemas/v1.5.0" golang-opentelemetry-otel-1.1.0/semconv/v1.5.0/trace.go000066400000000000000000001407101414226744000225500ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.5.0" import "go.opentelemetry.io/otel/attribute" // Span attributes used by AWS Lambda (in addition to general `faas` attributes). const ( // The full invoked ARN as provided on the `Context` passed to the function // (`Lambda-Runtime-Invoked-Function-ARN` header on the `/runtime/invocation/next` // applicable). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-east-1:123456:function:myfunction:myalias' // Note: This may be different from `faas.id` if an alias is involved. AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") ) // This document defines the attributes used to perform database client calls. const ( // An identifier for the database management system (DBMS) product being used. See // below for a list of well-known identifiers. // // Type: Enum // Required: Always // Stability: stable DBSystemKey = attribute.Key("db.system") // The connection string used to connect to the database. It is recommended to // remove embedded credentials. // // Type: string // Required: No // Stability: stable // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' DBConnectionStringKey = attribute.Key("db.connection_string") // Username for accessing the database. // // Type: string // Required: No // Stability: stable // Examples: 'readonly_user', 'reporting_user' DBUserKey = attribute.Key("db.user") // The fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver // used to connect. // // Type: string // Required: No // Stability: stable // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") // If no [tech-specific attribute](#call-level-attributes-for-specific- // technologies) is defined, this attribute is used to report the name of the // database being accessed. For commands that switch the database, this should be // set to the target database (even if the command fails). // // Type: string // Required: Required, if applicable and no more-specific attribute is defined. // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called "schema // name". DBNameKey = attribute.Key("db.name") // The database statement being executed. // // Type: string // Required: Required if applicable and not explicitly disabled via // instrumentation configuration. // Stability: stable // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' // Note: The value may be sanitized to exclude sensitive information. DBStatementKey = attribute.Key("db.statement") // The name of the operation being executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. // // Type: string // Required: Required, if `db.statement` is not applicable. // Stability: stable // Examples: 'findAndModify', 'HMSET', 'SELECT' // Note: When setting this to an SQL keyword, it is not recommended to attempt any // client-side parsing of `db.statement` just to get this property, but it should // be set if the operation name is provided by the library being instrumented. If // the SQL statement has an ambiguous operation, or performs more than one // operation, this value may be omitted. DBOperationKey = attribute.Key("db.operation") ) var ( // Some other SQL database. Fallback only. See notes DBSystemOtherSQL = DBSystemKey.String("other_sql") // Microsoft SQL Server DBSystemMSSQL = DBSystemKey.String("mssql") // MySQL DBSystemMySQL = DBSystemKey.String("mysql") // Oracle Database DBSystemOracle = DBSystemKey.String("oracle") // IBM DB2 DBSystemDB2 = DBSystemKey.String("db2") // PostgreSQL DBSystemPostgreSQL = DBSystemKey.String("postgresql") // Amazon Redshift DBSystemRedshift = DBSystemKey.String("redshift") // Apache Hive DBSystemHive = DBSystemKey.String("hive") // Cloudscape DBSystemCloudscape = DBSystemKey.String("cloudscape") // HyperSQL DataBase DBSystemHSQLDB = DBSystemKey.String("hsqldb") // Progress Database DBSystemProgress = DBSystemKey.String("progress") // SAP MaxDB DBSystemMaxDB = DBSystemKey.String("maxdb") // SAP HANA DBSystemHanaDB = DBSystemKey.String("hanadb") // Ingres DBSystemIngres = DBSystemKey.String("ingres") // FirstSQL DBSystemFirstSQL = DBSystemKey.String("firstsql") // EnterpriseDB DBSystemEDB = DBSystemKey.String("edb") // InterSystems Caché DBSystemCache = DBSystemKey.String("cache") // Adabas (Adaptable Database System) DBSystemAdabas = DBSystemKey.String("adabas") // Firebird DBSystemFirebird = DBSystemKey.String("firebird") // Apache Derby DBSystemDerby = DBSystemKey.String("derby") // FileMaker DBSystemFilemaker = DBSystemKey.String("filemaker") // Informix DBSystemInformix = DBSystemKey.String("informix") // InstantDB DBSystemInstantDB = DBSystemKey.String("instantdb") // InterBase DBSystemInterbase = DBSystemKey.String("interbase") // MariaDB DBSystemMariaDB = DBSystemKey.String("mariadb") // Netezza DBSystemNetezza = DBSystemKey.String("netezza") // Pervasive PSQL DBSystemPervasive = DBSystemKey.String("pervasive") // PointBase DBSystemPointbase = DBSystemKey.String("pointbase") // SQLite DBSystemSqlite = DBSystemKey.String("sqlite") // Sybase DBSystemSybase = DBSystemKey.String("sybase") // Teradata DBSystemTeradata = DBSystemKey.String("teradata") // Vertica DBSystemVertica = DBSystemKey.String("vertica") // H2 DBSystemH2 = DBSystemKey.String("h2") // ColdFusion IMQ DBSystemColdfusion = DBSystemKey.String("coldfusion") // Apache Cassandra DBSystemCassandra = DBSystemKey.String("cassandra") // Apache HBase DBSystemHBase = DBSystemKey.String("hbase") // MongoDB DBSystemMongoDB = DBSystemKey.String("mongodb") // Redis DBSystemRedis = DBSystemKey.String("redis") // Couchbase DBSystemCouchbase = DBSystemKey.String("couchbase") // CouchDB DBSystemCouchDB = DBSystemKey.String("couchdb") // Microsoft Azure Cosmos DB DBSystemCosmosDB = DBSystemKey.String("cosmosdb") // Amazon DynamoDB DBSystemDynamoDB = DBSystemKey.String("dynamodb") // Neo4j DBSystemNeo4j = DBSystemKey.String("neo4j") // Apache Geode DBSystemGeode = DBSystemKey.String("geode") // Elasticsearch DBSystemElasticsearch = DBSystemKey.String("elasticsearch") // Memcached DBSystemMemcached = DBSystemKey.String("memcached") // CockroachDB DBSystemCockroachdb = DBSystemKey.String("cockroachdb") ) // Connection-level attributes for Microsoft SQL Server const ( // The Microsoft SQL Server [instance name](https://docs.microsoft.com/en- // us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named instance. // // Type: string // Required: No // Stability: stable // Examples: 'MSSQLSERVER' // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer // required (but still recommended if non-standard). DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") ) // Call-level attributes for Cassandra const ( // The name of the keyspace being accessed. To be used instead of the generic // `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'mykeyspace' DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") // The fetch size used for paging, i.e. how many rows will be returned at once. // // Type: int // Required: No // Stability: stable // Examples: 5000 DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") // The consistency level of the query. Based on consistency values from // [CQL](https://docs.datastax.com/en/cassandra- // oss/3.0/cassandra/dml/dmlConfigConsistency.html). // // Type: Enum // Required: No // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'mytable' // Note: This mirrors the db.sql.table attribute but references cassandra rather // than sql. It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBCassandraTableKey = attribute.Key("db.cassandra.table") // Whether or not the query is idempotent. // // Type: boolean // Required: No // Stability: stable DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") // The number of times a query was speculatively executed. Not set or `0` if the // query was not executed speculatively. // // Type: int // Required: No // Stability: stable // Examples: 0, 2 DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") // The ID of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") // The data center of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'us-west-2' DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") ) var ( // all DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") // each_quorum DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") // quorum DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") // local_quorum DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") // one DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") // two DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") // three DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") // local_one DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") // any DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") // serial DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") // local_serial DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) // Call-level attributes for Apache HBase const ( // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being // accessed. To be used instead of the generic `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'default' DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") ) // Call-level attributes for Redis const ( // The index of the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To be used // instead of the generic `db.name` attribute. // // Type: int // Required: Required, if other than the default database (`0`). // Stability: stable // Examples: 0, 1, 15 DBRedisDBIndexKey = attribute.Key("db.redis.database_index") ) // Call-level attributes for MongoDB const ( // The collection being accessed within the database stated in `db.name`. // // Type: string // Required: Always // Stability: stable // Examples: 'customers', 'products' DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) // Call-level attrbiutes for SQL databases const ( // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'public.users', 'customers' // Note: It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBSQLTableKey = attribute.Key("db.sql.table") ) // This document defines the attributes used to report a single exception associated with a span. const ( // The type of the exception (its fully-qualified class name, if applicable). The // dynamic type of the exception should be preferred over the static type in // languages that support it. // // Type: string // Required: No // Stability: stable // Examples: 'java.net.ConnectException', 'OSError' ExceptionTypeKey = attribute.Key("exception.type") // The exception message. // // Type: string // Required: No // Stability: stable // Examples: 'Division by zero', "Can't convert 'int' object to str implicitly" ExceptionMessageKey = attribute.Key("exception.message") // A stacktrace as a string in the natural representation for the language // runtime. The representation is to be determined and documented by each language // SIG. // // Type: string // Required: No // Stability: stable // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test // exception\\n at ' // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' ExceptionStacktraceKey = attribute.Key("exception.stacktrace") // SHOULD be set to true if the exception event is recorded at a point where it is // known that the exception is escaping the scope of the span. // // Type: boolean // Required: No // Stability: stable // Note: An exception is considered to have escaped (or left) the scope of a span, // if that span is ended while the exception is still logically "in flight". // This may be actually "in flight" in some languages (e.g. if the exception // is passed to a Context manager's `__exit__` method in Python) but will // usually be caught at the point of recording the exception in most languages. // It is usually not possible to determine at the point where an exception is // thrown // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending the span, // as done in the [example above](#exception-end-example). // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, // since the event might have been recorded at a time where it was not // clear whether the exception will escape. ExceptionEscapedKey = attribute.Key("exception.escaped") ) // This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. const ( // Type of the trigger on which the function is executed. // // Type: Enum // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing // invocations, if it is known to the client. This is, for example, not the case, // when the transport layer is abstracted in a FaaS client framework without // access to its configuration. // Stability: stable FaaSTriggerKey = attribute.Key("faas.trigger") // The execution ID of the current function execution. // // Type: string // Required: No // Stability: stable // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' FaaSExecutionKey = attribute.Key("faas.execution") ) var ( // A response to some data source operation such as a database or filesystem read/write FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") // To provide an answer to an inbound HTTP request FaaSTriggerHTTP = FaaSTriggerKey.String("http") // A function is set to be executed when messages are sent to a messaging system FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") // A function is scheduled to be executed regularly FaaSTriggerTimer = FaaSTriggerKey.String("timer") // If none of the others apply FaaSTriggerOther = FaaSTriggerKey.String("other") ) // Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. const ( // The name of the source on which the triggering operation was performed. For // example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos // DB to the database name. // // Type: string // Required: Always // Stability: stable // Examples: 'myBucketName', 'myDBName' FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") // Describes the type of the operation that was performed on the data. // // Type: Enum // Required: Always // Stability: stable FaaSDocumentOperationKey = attribute.Key("faas.document.operation") // A string containing the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSDocumentTimeKey = attribute.Key("faas.document.time") // The document name/table subjected to the operation. For example, in Cloud // Storage or S3 is the name of the file, and in Cosmos DB the table name. // // Type: string // Required: No // Stability: stable // Examples: 'myFile.txt', 'myTableName' FaaSDocumentNameKey = attribute.Key("faas.document.name") ) var ( // When a new object is created FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") // When an object is modified FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") // When an object is deleted FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") ) // Semantic Convention for FaaS scheduled to be executed regularly. const ( // A string containing the function invocation time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSTimeKey = attribute.Key("faas.time") // A string containing the schedule period as [Cron Expression](https://docs.oracl // e.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). // // Type: string // Required: No // Stability: stable // Examples: '0/5 * * * ? *' FaaSCronKey = attribute.Key("faas.cron") ) // Contains additional attributes for incoming FaaS spans. const ( // A boolean that is true if the serverless function is executed for the first // time (aka cold-start). // // Type: boolean // Required: No // Stability: stable FaaSColdstartKey = attribute.Key("faas.coldstart") ) // Contains additional attributes for outgoing FaaS spans. const ( // The name of the invoked function. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked // function. FaaSInvokedNameKey = attribute.Key("faas.invoked_name") // The cloud provider of the invoked function. // // Type: Enum // Required: Always // Stability: stable // Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked // function. FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") // The cloud region of the invoked function. // // Type: string // Required: For some cloud providers, like AWS or GCP, the region in which a // function is hosted is essential to uniquely identify the function and also part // of its endpoint. Since it's part of the endpoint being called, the region is // always known to clients. In these cases, `faas.invoked_region` MUST be set // accordingly. If the region is unknown to the client or not required for // identifying the invoked function, setting `faas.invoked_region` is optional. // Stability: stable // Examples: 'eu-central-1' // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked // function. FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") ) var ( // Amazon Web Services FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") // Microsoft Azure FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") ) // These attributes may be used for any network related operation. const ( // Transport protocol used. See note below. // // Type: Enum // Required: No // Stability: stable NetTransportKey = attribute.Key("net.transport") // Remote address of the peer (dotted decimal for IPv4 or // [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6) // // Type: string // Required: No // Stability: stable // Examples: '127.0.0.1' NetPeerIPKey = attribute.Key("net.peer.ip") // Remote port number. // // Type: int // Required: No // Stability: stable // Examples: 80, 8080, 443 NetPeerPortKey = attribute.Key("net.peer.port") // Remote hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'example.com' NetPeerNameKey = attribute.Key("net.peer.name") // Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. // // Type: string // Required: No // Stability: stable // Examples: '192.168.0.1' NetHostIPKey = attribute.Key("net.host.ip") // Like `net.peer.port` but for the host port. // // Type: int // Required: No // Stability: stable // Examples: 35555 NetHostPortKey = attribute.Key("net.host.port") // Local hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'localhost' NetHostNameKey = attribute.Key("net.host.name") ) var ( // ip_tcp NetTransportTCP = NetTransportKey.String("ip_tcp") // ip_udp NetTransportUDP = NetTransportKey.String("ip_udp") // Another IP-based protocol NetTransportIP = NetTransportKey.String("ip") // Unix Domain socket. See below NetTransportUnix = NetTransportKey.String("unix") // Named or anonymous pipe. See note below NetTransportPipe = NetTransportKey.String("pipe") // In-process communication NetTransportInProc = NetTransportKey.String("inproc") // Something else (non IP-based) NetTransportOther = NetTransportKey.String("other") ) // Operations that access some remote service. const ( // The [`service.name`](../../resource/semantic_conventions/README.md#service) of // the remote service. SHOULD be equal to the actual `service.name` resource // attribute of the remote service if any. // // Type: string // Required: No // Stability: stable // Examples: 'AuthTokenCache' PeerServiceKey = attribute.Key("peer.service") ) // These attributes may be used for any operation with an authenticated and/or authorized enduser. const ( // Username or client_id extracted from the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the // inbound request from outside the system. // // Type: string // Required: No // Stability: stable // Examples: 'username' EnduserIDKey = attribute.Key("enduser.id") // Actual/assumed role the client is making the request under extracted from token // or application security context. // // Type: string // Required: No // Stability: stable // Examples: 'admin' EnduserRoleKey = attribute.Key("enduser.role") // Scopes or granted authorities the client currently possesses extracted from // token or application security context. The value would come from the scope // associated with an [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value // in a [SAML 2.0 Assertion](http://docs.oasis- // open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). // // Type: string // Required: No // Stability: stable // Examples: 'read:message, write:files' EnduserScopeKey = attribute.Key("enduser.scope") ) // These attributes may be used for any operation to store information about a thread that started a span. const ( // Current "managed" thread ID (as opposed to OS thread ID). // // Type: int // Required: No // Stability: stable // Examples: 42 ThreadIDKey = attribute.Key("thread.id") // Current thread name. // // Type: string // Required: No // Stability: stable // Examples: 'main' ThreadNameKey = attribute.Key("thread.name") ) // These attributes allow to report this unit of code and therefore to provide more context about the span. const ( // The method or function name, or equivalent (usually rightmost part of the code // unit's name). // // Type: string // Required: No // Stability: stable // Examples: 'serveRequest' CodeFunctionKey = attribute.Key("code.function") // The "namespace" within which `code.function` is defined. Usually the qualified // class or module name, such that `code.namespace` + some separator + // `code.function` form a unique identifier for the code unit. // // Type: string // Required: No // Stability: stable // Examples: 'com.example.MyHTTPService' CodeNamespaceKey = attribute.Key("code.namespace") // The source code file name that identifies the code unit as uniquely as possible // (preferably an absolute file path). // // Type: string // Required: No // Stability: stable // Examples: '/usr/local/MyApplication/content_root/app/index.php' CodeFilepathKey = attribute.Key("code.filepath") // The line number in `code.filepath` best representing the operation. It SHOULD // point within the code unit named in `code.function`. // // Type: int // Required: No // Stability: stable // Examples: 42 CodeLineNumberKey = attribute.Key("code.lineno") ) // This document defines semantic conventions for HTTP client and server Spans. const ( // HTTP request method. // // Type: string // Required: Always // Stability: stable // Examples: 'GET', 'POST', 'HEAD' HTTPMethodKey = attribute.Key("http.method") // Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. // Usually the fragment is not transmitted over HTTP, but if it is known, it // should be included nevertheless. // // Type: string // Required: No // Stability: stable // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' // Note: `http.url` MUST NOT contain credentials passed via URL in form of // `https://username:password@www.example.com/`. In such case the attribute's // value should be `https://www.example.com/`. HTTPURLKey = attribute.Key("http.url") // The full request target as passed in a HTTP request line or equivalent. // // Type: string // Required: No // Stability: stable // Examples: '/path/12314/?q=ddds#123' HTTPTargetKey = attribute.Key("http.target") // The value of the [HTTP host // header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is // empty or not present, this attribute should be the same. // // Type: string // Required: No // Stability: stable // Examples: 'www.example.org' HTTPHostKey = attribute.Key("http.host") // The URI scheme identifying the used protocol. // // Type: string // Required: No // Stability: stable // Examples: 'http', 'https' HTTPSchemeKey = attribute.Key("http.scheme") // [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). // // Type: int // Required: If and only if one was received/sent. // Stability: stable // Examples: 200 HTTPStatusCodeKey = attribute.Key("http.status_code") // Kind of HTTP protocol used. // // Type: Enum // Required: No // Stability: stable // Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` // except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. HTTPFlavorKey = attribute.Key("http.flavor") // Value of the [HTTP User- // Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the // client. // // Type: string // Required: No // Stability: stable // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' HTTPUserAgentKey = attribute.Key("http.user_agent") // The size of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") // The size of the uncompressed request payload body after transport decoding. Not // set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPRequestContentLengthUncompressedKey = attribute.Key("http.request_content_length_uncompressed") // The size of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") // The size of the uncompressed response payload body after transport decoding. // Not set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") ) var ( // HTTP 1.0 HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") // HTTP 1.1 HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") // HTTP 2 HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") // SPDY protocol HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") // QUIC protocol HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") ) // Semantic Convention for HTTP Server const ( // The primary server name of the matched virtual host. This should be obtained // via configuration. If no such configuration can be obtained, this attribute // MUST NOT be set ( `net.host.name` should be used instead). // // Type: string // Required: No // Stability: stable // Examples: 'example.com' // Note: `http.url` is usually not readily available on the server side but would // have to be assembled in a cumbersome and sometimes lossy process from other // information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus // preferred to supply the raw data that is available. HTTPServerNameKey = attribute.Key("http.server_name") // The matched route (path template). // // Type: string // Required: No // Stability: stable // Examples: '/users/:userID?' HTTPRouteKey = attribute.Key("http.route") // The IP address of the original client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en- // US/docs/Web/HTTP/Headers/X-Forwarded-For)). // // Type: string // Required: No // Stability: stable // Examples: '83.164.160.102' // Note: This is not necessarily the same as `net.peer.ip`, which would identify // the network-level peer, which may be a proxy. HTTPClientIPKey = attribute.Key("http.client_ip") ) // Attributes that exist for multiple DynamoDB request types. const ( // The keys in the `RequestItems` object field. // // Type: string[] // Required: No // Stability: stable // Examples: 'Users', 'Cats' AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") // The JSON-serialized value of each item in the `ConsumedCapacity` response // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : { // "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": // "string", "WriteCapacityUnits": number }' AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") // The JSON-serialized value of the `ItemCollectionMetrics` response field. // // Type: string // Required: No // Stability: stable // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }' AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") // The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") // The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") // The value of the `ConsistentRead` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") // The value of the `ProjectionExpression` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Title', 'Title, Price, Color', 'Title, Description, RelatedItems, // ProductReviews' AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") // The value of the `Limit` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") // The value of the `AttributesToGet` request parameter. // // Type: string[] // Required: No // Stability: stable // Examples: 'lives', 'id' AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") // The value of the `IndexName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'name_to_group' AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") // The value of the `Select` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'ALL_ATTRIBUTES', 'COUNT' AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") ) // DynamoDB.CreateTable const ( // The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request // field // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" }, "ProvisionedThroughput": { "ReadCapacityUnits": // number, "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") // The JSON-serialized value of each item of the `LocalSecondaryIndexes` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexARN": "string", "IndexName": "string", "IndexSizeBytes": // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" } }' AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") ) // DynamoDB.ListTables const ( // The value of the `ExclusiveStartTableName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Users', 'CatsTable' AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") // The the number of items in the `TableNames` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 20 AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") ) // DynamoDB.Query const ( // The value of the `ScanIndexForward` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") ) // DynamoDB.Scan const ( // The value of the `Segment` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") // The value of the `TotalSegments` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 100 AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") // The value of the `Count` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") // The value of the `ScannedCount` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 50 AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") ) // DynamoDB.UpdateTable const ( // The JSON-serialized value of each item in the `AttributeDefinitions` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") // The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` // request field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }' AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") ) // This document defines the attributes used in messaging systems. const ( // A string identifying the messaging system. // // Type: string // Required: Always // Stability: stable // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // The message destination name. This might be equal to the span name but is // required nevertheless. // // Type: string // Required: Always // Stability: stable // Examples: 'MyQueue', 'MyTopic' MessagingDestinationKey = attribute.Key("messaging.destination") // The kind of message destination // // Type: Enum // Required: Required only if the message destination is either a `queue` or // `topic`. // Stability: stable MessagingDestinationKindKey = attribute.Key("messaging.destination_kind") // A boolean that is true if the message destination is temporary. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingTempDestinationKey = attribute.Key("messaging.temp_destination") // The name of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: 'AMQP', 'MQTT' MessagingProtocolKey = attribute.Key("messaging.protocol") // The version of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: '0.9.1' MessagingProtocolVersionKey = attribute.Key("messaging.protocol_version") // Connection string. // // Type: string // Required: No // Stability: stable // Examples: 'tibjmsnaming://localhost:7222', // 'https://queue.amazonaws.com/80398EXAMPLE/MyQueue' MessagingURLKey = attribute.Key("messaging.url") // A value used by the messaging system as an identifier for the message, // represented as a string. // // Type: string // Required: No // Stability: stable // Examples: '452a7c7c7c7048c2f887f61572b18fc2' MessagingMessageIDKey = attribute.Key("messaging.message_id") // The [conversation ID](#conversations) identifying the conversation to which the // message belongs, represented as a string. Sometimes called "Correlation ID". // // Type: string // Required: No // Stability: stable // Examples: 'MyConversationID' MessagingConversationIDKey = attribute.Key("messaging.conversation_id") // The (uncompressed) size of the message payload in bytes. Also use this // attribute if it is unknown whether the compressed or uncompressed payload size // is reported. // // Type: int // Required: No // Stability: stable // Examples: 2738 MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message_payload_size_bytes") // The compressed size of the message payload in bytes. // // Type: int // Required: No // Stability: stable // Examples: 2048 MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message_payload_compressed_size_bytes") ) var ( // A message sent to a queue MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") // A message sent to a topic MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") ) // Semantic convention for a consumer of messages received from a messaging system const ( // A string identifying the kind of message consumption as defined in the // [Operation names](#operation-names) section above. If the operation is "send", // this attribute MUST NOT be set, since the operation can be inferred from the // span kind in that case. // // Type: Enum // Required: No // Stability: stable MessagingOperationKey = attribute.Key("messaging.operation") ) var ( // receive MessagingOperationReceive = MessagingOperationKey.String("receive") // process MessagingOperationProcess = MessagingOperationKey.String("process") ) // Attributes for RabbitMQ const ( // RabbitMQ message routing key. // // Type: string // Required: Unless it is empty. // Stability: stable // Examples: 'myKey' MessagingRabbitmqRoutingKeyKey = attribute.Key("messaging.rabbitmq.routing_key") ) // Attributes for Apache Kafka const ( // Message keys in Kafka are used for grouping alike messages to ensure they're // processed on the same partition. They differ from `messaging.message_id` in // that they're not unique. If the key is `null`, the attribute MUST NOT be set. // // Type: string // Required: No // Stability: stable // Examples: 'myKey' // Note: If the key type is not string, it's string representation has to be // supplied for the attribute. If the key has no unambiguous, canonical string // form, don't include its value. MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message_key") // Name of the Kafka Consumer Group that is handling the message. Only applies to // consumers, not producers. // // Type: string // Required: No // Stability: stable // Examples: 'my-group' MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer_group") // Client ID for the Consumer or Producer that is handling the message. // // Type: string // Required: No // Stability: stable // Examples: 'client-5' MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") // Partition the message is sent to. // // Type: int // Required: No // Stability: stable // Examples: 2 MessagingKafkaPartitionKey = attribute.Key("messaging.kafka.partition") // A boolean that is true if the message is a tombstone. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") ) // This document defines semantic conventions for remote procedure calls. const ( // A string identifying the remoting system. // // Type: string // Required: Always // Stability: stable // Examples: 'grpc', 'java_rmi', 'wcf' RPCSystemKey = attribute.Key("rpc.system") // The full (logical) name of the service being called, including its package // name, if applicable. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'myservice.EchoService' // Note: This is the logical name of the service from the RPC interface // perspective, which can be different from the name of any implementing class. // The `code.namespace` attribute may be used to store the latter (despite the // attribute name, it may include a class name; e.g., class with method actually // executing the call on the server side, RPC client stub class on the client // side). RPCServiceKey = attribute.Key("rpc.service") // The name of the (logical) method being called, must be equal to the $method // part in the span name. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'exampleMethod' // Note: This is the logical name of the method from the RPC interface // perspective, which can be different from the name of any implementing // method/function. The `code.function` attribute may be used to store the latter // (e.g., method actually executing the call on the server side, RPC client stub // method on the client side). RPCMethodKey = attribute.Key("rpc.method") ) // Tech-specific attributes for gRPC. const ( // The [numeric status // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC // request. // // Type: Enum // Required: Always // Stability: stable RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) var ( // OK RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) // CANCELLED RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) // UNKNOWN RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) // INVALID_ARGUMENT RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) // DEADLINE_EXCEEDED RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) // NOT_FOUND RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) // ALREADY_EXISTS RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) // PERMISSION_DENIED RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) // RESOURCE_EXHAUSTED RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) // FAILED_PRECONDITION RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) // ABORTED RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) // OUT_OF_RANGE RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) // UNIMPLEMENTED RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) // INTERNAL RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) // UNAVAILABLE RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) // DATA_LOSS RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) // UNAUTHENTICATED RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) ) // Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). const ( // Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC // 1.0 does not specify this, the value can be omitted. // // Type: string // Required: If missing, it is assumed to be "1.0". // Stability: stable // Examples: '2.0', '1.0' RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") // `id` property of request or response. Since protocol allows id to be int, // string, `null` or missing (for notifications), value is expected to be cast to // string for simplicity. Use empty string in case of `null` value. Omit entirely // if this is a notification. // // Type: string // Required: No // Stability: stable // Examples: '10', 'request-7', '' RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") // `error.code` property of response if it is an error response. // // Type: int // Required: If missing, response is assumed to be successful. // Stability: stable // Examples: -32700, 100 RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") // `error.message` property of response if it is an error response. // // Type: string // Required: No // Stability: stable // Examples: 'Parse error', 'User already exists' RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") ) golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/000077500000000000000000000000001414226744000211225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/doc.go000066400000000000000000000016621414226744000222230ustar00rootroot00000000000000// 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 semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions // as of the v1.6.1 version of the OpenTelemetry specification. package semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/exception.go000066400000000000000000000014271414226744000234530ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" const ( // ExceptionEventName is the name of the Span event representing an exception. ExceptionEventName = "exception" ) golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/http.go000066400000000000000000000217301414226744000224330ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" import ( "fmt" "net" "net/http" "strconv" "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // HTTP scheme attributes. var ( HTTPSchemeHTTP = HTTPSchemeKey.String("http") HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} switch network { case "tcp", "tcp4", "tcp6": attrs = append(attrs, NetTransportTCP) case "udp", "udp4", "udp6": attrs = append(attrs, NetTransportUDP) case "ip", "ip4", "ip6": attrs = append(attrs, NetTransportIP) case "unix", "unixgram", "unixpacket": attrs = append(attrs, NetTransportUnix) default: attrs = append(attrs, NetTransportOther) } peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) if peerIP != "" { attrs = append(attrs, NetPeerIPKey.String(peerIP)) } if peerName != "" { attrs = append(attrs, NetPeerNameKey.String(peerName)) } if peerPort != 0 { attrs = append(attrs, NetPeerPortKey.Int(peerPort)) } hostIP, hostName, hostPort := "", "", 0 for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { hostIP, hostName, hostPort = hostIPNamePort(someHost) if hostIP != "" || hostName != "" || hostPort != 0 { break } } if hostIP != "" { attrs = append(attrs, NetHostIPKey.String(hostIP)) } if hostName != "" { attrs = append(attrs, NetHostNameKey.String(hostName)) } if hostPort != 0 { attrs = append(attrs, NetHostPortKey.Int(hostPort)) } return attrs } // hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. // It handles both IPv4 and IPv6 addresses. If the host portion is not recognized // as a valid IPv4 or IPv6 address, the `ip` result will be empty and the // host portion will instead be returned in `name`. func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { var ( hostPart, portPart string parsedPort uint64 err error ) if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { hostPart, portPart = hostWithPort, "" } if parsedIP := net.ParseIP(hostPart); parsedIP != nil { ip = parsedIP.String() } else { name = hostPart } if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { port = int(parsedPort) } return } // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { if username, _, ok := request.BasicAuth(); ok { return []attribute.KeyValue{EnduserIDKey.String(username)} } return nil } // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if request.Method != "" { attrs = append(attrs, HTTPMethodKey.String(request.Method)) } else { attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) } // remove any username/password info that may be in the URL // before adding it to the attributes userinfo := request.URL.User request.URL.User = nil attrs = append(attrs, HTTPURLKey.String(request.URL.String())) // restore any username/password info that was removed request.URL.User = userinfo return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if ua := request.UserAgent(); ua != "" { attrs = append(attrs, HTTPUserAgentKey.String(ua)) } if request.ContentLength > 0 { attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality attrs := []attribute.KeyValue{} if request.TLS != nil { attrs = append(attrs, HTTPSchemeHTTPS) } else { attrs = append(attrs, HTTPSchemeHTTP) } if request.Host != "" { attrs = append(attrs, HTTPHostKey.String(request.Host)) } flavor := "" if request.ProtoMajor == 1 { flavor = fmt.Sprintf("1.%d", request.ProtoMinor) } else if request.ProtoMajor == 2 { flavor = "2" } if flavor != "" { attrs = append(attrs, HTTPFlavorKey.String(flavor)) } return attrs } // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } // HTTPServerAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the server side. Currently, only basic authentication is // supported. func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPMethodKey.String(request.Method), HTTPTargetKey.String(request.RequestURI), } if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } if route != "" { attrs = append(attrs, HTTPRouteKey.String(route)) } if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) } } return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPStatusCodeKey.Int(code), } return attrs } type codeRange struct { fromInclusive int toInclusive int } func (r codeRange) contains(code int) bool { return r.fromInclusive <= code && code <= r.toInclusive } var validRangesPerCategory = map[int][]codeRange{ 1: { {http.StatusContinue, http.StatusEarlyHints}, }, 2: { {http.StatusOK, http.StatusAlreadyReported}, {http.StatusIMUsed, http.StatusIMUsed}, }, 3: { {http.StatusMultipleChoices, http.StatusUseProxy}, {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, }, 4: { {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, {http.StatusPreconditionRequired, http.StatusTooManyRequests}, {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, }, 5: { {http.StatusInternalServerError, http.StatusLoopDetected}, {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, }, } // SpanStatusFromHTTPStatusCode generates a status code and a message // as specified by the OpenTelemetry specification for a span. func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { spanCode, valid := validateHTTPStatusCode(code) if !valid { return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) } return spanCode, "" } // Validates the HTTP status code and returns corresponding span status code. // If the `code` is not a valid HTTP status code, returns span status Error // and false. func validateHTTPStatusCode(code int) (codes.Code, bool) { category := code / 100 ranges, ok := validRangesPerCategory[category] if !ok { return codes.Error, false } ok = false for _, crange := range ranges { ok = crange.contains(code) if ok { break } } if !ok { return codes.Error, false } if category > 0 && category < 4 { return codes.Unset, true } return codes.Error, true } golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/http_test.go000066400000000000000000000725421414226744000235010ustar00rootroot00000000000000// 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 semconv import ( "crypto/tls" "net/http" "net/url" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) type tlsOption int const ( noTLS tlsOption = iota withTLS ) func TestNetAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string network string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped, tcp", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), }, }, { name: "stripped, udp", network: "udp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_udp"), }, }, { name: "stripped, ip", network: "ip", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip"), }, }, { name: "stripped, unix", network: "unix", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "unix"), }, }, { name: "stripped, other", network: "nih", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "other"), }, }, { name: "with remote ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[fe80::0202:b3ff:fe1e:8329]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4-in-v6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[::ffff:192.168.0.1]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), }, }, { name: "with remote ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "fe80::0202:b3ff:fe1e:8329", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with remote ipv4_in_v6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "::ffff:192.168.0.1", // section 2.5.5.2 of RFC4291 host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), }, }, { name: "with remote name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), }, }, { name: "with remote port only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: ":56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.Int("net.peer.port", 56), }, }, { name: "with host name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "fe80::0202:b3ff:fe1e:8329", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with host name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, { name: "with host name and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with empty host and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: ":80", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.Int("net.host.port", 80), }, }, { name: "with host ip and port in headers", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "Host": []string{"4.3.2.1:78"}, }, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "4.3.2.1:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "[fe80::202:b3ff:fe1e:8329]:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS) got := NetAttributesFromHTTPRequest(tc.network, r) if diff := cmp.Diff( tc.expected, got, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("attributes differ: diff %+v,", diff) } }) } } func TestEndUserAttributesFromHTTPRequest(t *testing.T) { r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS) var expected []attribute.KeyValue got := EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) r.SetBasicAuth("admin", "password") expected = []attribute.KeyValue{attribute.String("enduser.id", "admin")} got = EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) } func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string serverName string route string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped", serverName: "", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with server name", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with tls", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with route", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), }, }, { name: "with host", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with proxy info", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"203.0.113.195, 70.41.3.18, 150.172.238.178"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "203.0.113.195"), }, }, { name: "with http 1.1", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with http 2", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with content length", method: "GET", requestURI: "/user/123", contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, } for idx, tc := range testcases { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r) assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) } } func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) { expected := []attribute.KeyValue{ attribute.Int("http.status_code", 404), } got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound) assertElementsMatch(t, expected, got, "with valid HTTP status code") assert.ElementsMatch(t, expected, got) expected = []attribute.KeyValue{ attribute.Int("http.status_code", 499), } got = HTTPAttributesFromHTTPStatusCode(499) assertElementsMatch(t, expected, got, "with invalid HTTP status code") } func TestSpanStatusFromHTTPStatusCode(t *testing.T) { for code := 0; code < 1000; code++ { expected := getExpectedCodeForHTTPCode(code) got, msg := SpanStatusFromHTTPStatusCode(code) assert.Equalf(t, expected, got, "%s vs %s", expected, got) _, valid := validateHTTPStatusCode(code) if !valid { assert.NotEmpty(t, msg, "message should be set if error cannot be inferred from code") } else { assert.Empty(t, msg, "message should not be set if error can be inferred from code") } } } func getExpectedCodeForHTTPCode(code int) codes.Code { if http.StatusText(code) == "" { return codes.Error } switch code { case http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusTooManyRequests, http.StatusNotImplemented, http.StatusServiceUnavailable, http.StatusGatewayTimeout: return codes.Error } category := code / 100 if category > 0 && category < 4 { return codes.Unset } return codes.Error } func assertElementsMatch(t *testing.T, expected, got []attribute.KeyValue, format string, args ...interface{}) { if !assert.ElementsMatchf(t, expected, got, format, args...) { t.Log("expected:", kvStr(expected)) t.Log("got:", kvStr(got)) } } func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request { major, minor := protoToInts(proto) var tlsConn *tls.ConnectionState switch tlsopt { case noTLS: case withTLS: tlsConn = &tls.ConnectionState{} } return &http.Request{ Method: method, URL: u, Proto: proto, ProtoMajor: major, ProtoMinor: minor, Header: header, Host: host, RemoteAddr: remoteAddr, RequestURI: requestURI, TLS: tlsConn, } } func protoToInts(proto string) (int, int) { switch proto { case "HTTP/1.0": return 1, 0 case "HTTP/1.1": return 1, 1 case "HTTP/2.0": return 2, 0 } // invalid proto return 13, 42 } func kvStr(kvs []attribute.KeyValue) string { sb := strings.Builder{} sb.WriteRune('[') for idx, label := range kvs { if idx > 0 { sb.WriteString(", ") } sb.WriteString((string)(label.Key)) sb.WriteString(": ") sb.WriteString(label.Value.Emit()) } sb.WriteRune(']') return sb.String() } func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) { testCases := []struct { name string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue }{ { name: "stripped", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with tls", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), }, }, { name: "with host", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 1.1", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 2", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with content length", method: "GET", url: &url.URL{ Path: "/user/123", }, contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, { name: "with empty method (fallback to GET)", method: "", url: &url.URL{ Path: "/user/123", }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, { name: "authentication information is stripped", method: "", url: &url.URL{ Path: "/user/123", User: url.UserPassword("foo", "bar"), }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPClientAttributesFromHTTPRequest(r) assert.ElementsMatch(t, tc.expected, got) }) } } golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/resource.go000066400000000000000000000761521414226744000233130ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" import "go.opentelemetry.io/otel/attribute" // A cloud environment (e.g. GCP, Azure, AWS) const ( // Name of the cloud provider. // // Type: Enum // Required: No // Stability: stable CloudProviderKey = attribute.Key("cloud.provider") // The cloud account ID the resource is assigned to. // // Type: string // Required: No // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") // The geographical region the resource is running. Refer to your provider's docs // to see the available regions, for example [Alibaba Cloud // regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), // [Azure regions](https://azure.microsoft.com/en-us/global- // infrastructure/geographies/), or [Google Cloud // regions](https://cloud.google.com/about/locations). // // Type: string // Required: No // Stability: stable // Examples: 'us-central1', 'us-east-1' CloudRegionKey = attribute.Key("cloud.region") // Cloud regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the resource // is running. // // Type: string // Required: No // Stability: stable // Examples: 'us-east-1c' // Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud. CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") // The cloud platform in use. // // Type: Enum // Required: No // Stability: stable // Note: The prefix of the service SHOULD match the one specified in // `cloud.provider`. CloudPlatformKey = attribute.Key("cloud.platform") ) var ( // Alibaba Cloud CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud") // Amazon Web Services CloudProviderAWS = CloudProviderKey.String("aws") // Microsoft Azure CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") ) var ( // Alibaba Cloud Elastic Compute Service CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs") // Alibaba Cloud Function Compute CloudPlatformAlibabaCloudFc = CloudPlatformKey.String("alibaba_cloud_fc") // AWS Elastic Compute Cloud CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") // AWS Elastic Container Service CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") // AWS Elastic Kubernetes Service CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") // AWS Lambda CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") // Azure Kubernetes Service CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") // Azure Functions CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") // Azure App Service CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") // Google Cloud Compute Engine (GCE) CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") // Google Cloud Run CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") // Google Cloud Kubernetes Engine (GKE) CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") // Google Cloud Functions (GCF) CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") ) // Resources used by AWS Elastic Container Service (ECS). const ( // The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws. // amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") // The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo // perguide/clusters.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") // The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l // aunch_types.html) for an ECS task. // // Type: Enum // Required: No // Stability: stable AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") // The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates // t/developerguide/task_definitions.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") // The task definition family this task definition is a member of. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-family' AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") // The revision for this task definition. // // Type: string // Required: No // Stability: stable // Examples: '8', '26' AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") ) var ( // ec2 AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") // fargate AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") ) // Resources used by AWS Elastic Kubernetes Service (EKS). const ( // The ARN of an EKS cluster. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") ) // Resources specific to Amazon Web Services. const ( // The name(s) of the AWS log group(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: '/aws/lambda/my-function', 'opentelemetry-service' // Note: Multiple log groups must be supported for cases like multi-container // applications, where a single application has sidecar containers, and each write // to their own log group. AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") // The Amazon Resource Name(s) (ARN) of the AWS log group(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' // Note: See the [log group ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") // The name(s) of the AWS log stream(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") // The ARN(s) of the AWS log stream(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log- // stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' // Note: See the [log stream ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain // several log streams, so these ARNs necessarily identify both a log group and a // log stream. AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") ) // A container instance. const ( // Container name. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-autoconf' ContainerNameKey = attribute.Key("container.name") // Container ID. Usually a UUID, as for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container- // identification). The UUID might be abbreviated. // // Type: string // Required: No // Stability: stable // Examples: 'a3bf90e006b2' ContainerIDKey = attribute.Key("container.id") // The container runtime managing this container. // // Type: string // Required: No // Stability: stable // Examples: 'docker', 'containerd', 'rkt' ContainerRuntimeKey = attribute.Key("container.runtime") // Name of the image the container was built on. // // Type: string // Required: No // Stability: stable // Examples: 'gcr.io/opentelemetry/operator' ContainerImageNameKey = attribute.Key("container.image.name") // Container image tag. // // Type: string // Required: No // Stability: stable // Examples: '0.1' ContainerImageTagKey = attribute.Key("container.image.tag") ) // The software deployment. const ( // Name of the [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). // // Type: string // Required: No // Stability: stable // Examples: 'staging', 'production' DeploymentEnvironmentKey = attribute.Key("deployment.environment") ) // The device on which the process represented by this resource is running. const ( // A unique identifier representing the device // // Type: string // Required: No // Stability: stable // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' // Note: The device identifier MUST only be defined using the values outlined // below. This value is not an advertising identifier and MUST NOT be used as // such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id // entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden // tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the // Firebase Installation ID or a globally unique UUID which is persisted across // sessions in your application. More information can be found // [here](https://developer.android.com/training/articles/user-data-ids) on best // practices and exact implementation details. Caution should be taken when // storing personal data or anything which can identify a user. GDPR and data // protection laws may apply, ensure you do your own due diligence. DeviceIDKey = attribute.Key("device.id") // The model identifier for the device // // Type: string // Required: No // Stability: stable // Examples: 'iPhone3,4', 'SM-G920F' // Note: It's recommended this value represents a machine readable version of the // model identifier rather than the market or consumer-friendly name of the // device. DeviceModelIdentifierKey = attribute.Key("device.model.identifier") // The marketing name for the device model // // Type: string // Required: No // Stability: stable // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' // Note: It's recommended this value represents a human readable version of the // device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") ) // A serverless instance. const ( // The name of the single function that this runtime instance executes. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: This is the name of the function as configured/deployed on the FaaS // platform and is usually different from the name of the callback function (which // may be stored in the // [`code.namespace`/`code.function`](../../trace/semantic_conventions/span- // general.md#source-code-attributes) span attributes). FaaSNameKey = attribute.Key("faas.name") // The unique ID of the single function that this runtime instance executes. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' // Note: Depending on the cloud provider, use: // * **AWS Lambda:** The function // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and- // namespaces.html). // Take care not to use the "invoked ARN" directly but replace any // [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // aliases.html) with the resolved function version, as the same runtime instance // may be invokable with multiple // different aliases. // * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full- // resource-names) // * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en- // us/rest/api/resources/resources/get-by-id). // On some providers, it may not be possible to determine the full ID at startup, // which is why this field cannot be made required. For example, on AWS the // account ID // part of the ARN is not available without calling another AWS API // which may be deemed too slow for a short-running lambda function. // As an alternative, consider setting `faas.id` as a span attribute instead. FaaSIDKey = attribute.Key("faas.id") // The immutable version of the function being executed. // // Type: string // Required: No // Stability: stable // Examples: '26', 'pinkfroid-00002' // Note: Depending on the cloud provider and platform, use: // * **AWS Lambda:** The [function // version](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // versions.html) // (an integer represented as a decimal string). // * **Google Cloud Run:** The // [revision](https://cloud.google.com/run/docs/managing/revisions) // (i.e., the function name plus the revision suffix). // * **Google Cloud Functions:** The value of the // [`K_REVISION` environment // variable](https://cloud.google.com/functions/docs/env- // var#runtime_environment_variables_set_automatically). // * **Azure Functions:** Not applicable. Do not set this attribute. FaaSVersionKey = attribute.Key("faas.version") // The execution environment ID as a string, that will be potentially reused for // other invocations to the same function/function version. // // Type: string // Required: No // Stability: stable // Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de' // Note: * **AWS Lambda:** Use the (full) log stream name. FaaSInstanceKey = attribute.Key("faas.instance") // The amount of memory available to the serverless function in MiB. // // Type: int // Required: No // Stability: stable // Examples: 128 // Note: It's recommended to set this attribute since e.g. too little memory can // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this // information. FaaSMaxMemoryKey = attribute.Key("faas.max_memory") ) // A host is defined as a general computing instance. const ( // Unique host ID. For Cloud, this must be the instance_id assigned by the cloud // provider. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostIDKey = attribute.Key("host.id") // Name of the host. On Unix systems, it may contain what the hostname command // returns, or the fully qualified hostname, or another name specified by the // user. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostNameKey = attribute.Key("host.name") // Type of host. For Cloud, this must be the machine type. // // Type: string // Required: No // Stability: stable // Examples: 'n1-standard-1' HostTypeKey = attribute.Key("host.type") // The CPU architecture the host system is running on. // // Type: Enum // Required: No // Stability: stable HostArchKey = attribute.Key("host.arch") // Name of the VM image or OS install the host was instantiated from. // // Type: string // Required: No // Stability: stable // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' HostImageNameKey = attribute.Key("host.image.name") // VM image ID. For Cloud, this value is from the provider. // // Type: string // Required: No // Stability: stable // Examples: 'ami-07b06b442921831e5' HostImageIDKey = attribute.Key("host.image.id") // The version string of the VM image as defined in [Version // Attributes](README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '0.1' HostImageVersionKey = attribute.Key("host.image.version") ) var ( // AMD64 HostArchAMD64 = HostArchKey.String("amd64") // ARM32 HostArchARM32 = HostArchKey.String("arm32") // ARM64 HostArchARM64 = HostArchKey.String("arm64") // Itanium HostArchIA64 = HostArchKey.String("ia64") // 32-bit PowerPC HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) // A Kubernetes Cluster. const ( // The name of the cluster. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-cluster' K8SClusterNameKey = attribute.Key("k8s.cluster.name") ) // A Kubernetes Node object. const ( // The name of the Node. // // Type: string // Required: No // Stability: stable // Examples: 'node-1' K8SNodeNameKey = attribute.Key("k8s.node.name") // The UID of the Node. // // Type: string // Required: No // Stability: stable // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' K8SNodeUIDKey = attribute.Key("k8s.node.uid") ) // A Kubernetes Namespace. const ( // The name of the namespace that the pod is running in. // // Type: string // Required: No // Stability: stable // Examples: 'default' K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") ) // A Kubernetes Pod object. const ( // The UID of the Pod. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SPodUIDKey = attribute.Key("k8s.pod.uid") // The name of the Pod. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-pod-autoconf' K8SPodNameKey = attribute.Key("k8s.pod.name") ) // A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( // The name of the Container in a Pod template. // // Type: string // Required: No // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") ) // A Kubernetes ReplicaSet object. const ( // The UID of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") // The name of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") ) // A Kubernetes Deployment object. const ( // The UID of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") // The name of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") ) // A Kubernetes StatefulSet object. const ( // The UID of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") // The name of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") ) // A Kubernetes DaemonSet object. const ( // The UID of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") // The name of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") ) // A Kubernetes Job object. const ( // The UID of the Job. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SJobUIDKey = attribute.Key("k8s.job.uid") // The name of the Job. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SJobNameKey = attribute.Key("k8s.job.name") ) // A Kubernetes CronJob object. const ( // The UID of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") // The name of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") ) // The operating system (OS) on which the process represented by this resource is running. const ( // The operating system type. // // Type: Enum // Required: Always // Stability: stable OSTypeKey = attribute.Key("os.type") // Human readable (not intended to be parsed) OS version information, like e.g. // reported by `ver` or `lsb_release -a` commands. // // Type: string // Required: No // Stability: stable // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS' OSDescriptionKey = attribute.Key("os.description") // Human readable operating system name. // // Type: string // Required: No // Stability: stable // Examples: 'iOS', 'Android', 'Ubuntu' OSNameKey = attribute.Key("os.name") // The version string of the operating system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '14.2.1', '18.04.1' OSVersionKey = attribute.Key("os.version") ) var ( // Microsoft Windows OSTypeWindows = OSTypeKey.String("windows") // Linux OSTypeLinux = OSTypeKey.String("linux") // Apple Darwin OSTypeDarwin = OSTypeKey.String("darwin") // FreeBSD OSTypeFreeBSD = OSTypeKey.String("freebsd") // NetBSD OSTypeNetBSD = OSTypeKey.String("netbsd") // OpenBSD OSTypeOpenBSD = OSTypeKey.String("openbsd") // DragonFly BSD OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") // HP-UX (Hewlett Packard Unix) OSTypeHPUX = OSTypeKey.String("hpux") // AIX (Advanced Interactive eXecutive) OSTypeAIX = OSTypeKey.String("aix") // Oracle Solaris OSTypeSolaris = OSTypeKey.String("solaris") // IBM z/OS OSTypeZOS = OSTypeKey.String("z_os") ) // An operating system process. const ( // Process identifier (PID). // // Type: int // Required: No // Stability: stable // Examples: 1234 ProcessPIDKey = attribute.Key("process.pid") // The name of the process executable. On Linux based systems, can be set to the // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: 'otelcol' ProcessExecutableNameKey = attribute.Key("process.executable.name") // The full path to the process executable. On Linux based systems, can be set to // the target of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: '/usr/bin/cmd/otelcol' ProcessExecutablePathKey = attribute.Key("process.executable.path") // The command used to launch the process (i.e. the command name). On Linux based // systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, // can be set to the first parameter extracted from `GetCommandLineW`. // // Type: string // Required: See below // Stability: stable // Examples: 'cmd/otelcol' ProcessCommandKey = attribute.Key("process.command") // The full command used to launch the process as a single string representing the // full command. On Windows, can be set to the result of `GetCommandLineW`. Do not // set this if you have to assemble it just for monitoring; use // `process.command_args` instead. // // Type: string // Required: See below // Stability: stable // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' ProcessCommandLineKey = attribute.Key("process.command_line") // All the command arguments (including the command/executable itself) as received // by the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited strings // extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be // the full argv vector passed to `main`. // // Type: string[] // Required: See below // Stability: stable // Examples: 'cmd/otecol', '--config=config.yaml' ProcessCommandArgsKey = attribute.Key("process.command_args") // The username of the user that owns the process. // // Type: string // Required: No // Stability: stable // Examples: 'root' ProcessOwnerKey = attribute.Key("process.owner") ) // The single (language) runtime instance which is monitored. const ( // The name of the runtime of this process. For compiled native binaries, this // SHOULD be the name of the compiler. // // Type: string // Required: No // Stability: stable // Examples: 'OpenJDK Runtime Environment' ProcessRuntimeNameKey = attribute.Key("process.runtime.name") // The version of the runtime of this process, as returned by the runtime without // modification. // // Type: string // Required: No // Stability: stable // Examples: '14.0.2' ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") // An additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. // // Type: string // Required: No // Stability: stable // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") ) // A service instance. const ( // Logical name of the service. // // Type: string // Required: Always // Stability: stable // Examples: 'shoppingcart' // Note: MUST be the same for all instances of horizontally scaled services. If // the value was not specified, SDKs MUST fallback to `unknown_service:` // concatenated with [`process.executable.name`](process.md#process), e.g. // `unknown_service:bash`. If `process.executable.name` is not available, the // value MUST be set to `unknown_service`. ServiceNameKey = attribute.Key("service.name") // A namespace for `service.name`. // // Type: string // Required: No // Stability: stable // Examples: 'Shop' // Note: A string value having a meaning that helps to distinguish a group of // services, for example the team name that owns a group of services. // `service.name` is expected to be unique within the same namespace. If // `service.namespace` is not specified in the Resource then `service.name` is // expected to be unique for all services that have no explicit namespace defined // (so the empty/unspecified namespace is simply one more valid namespace). Zero- // length namespace string is assumed equal to unspecified namespace. ServiceNamespaceKey = attribute.Key("service.namespace") // The string ID of the service instance. // // Type: string // Required: No // Stability: stable // Examples: '627cc493-f310-47de-96bd-71410b7dec09' // Note: MUST be unique for each instance of the same // `service.namespace,service.name` pair (in other words // `service.namespace,service.name,service.instance.id` triplet MUST be globally // unique). The ID helps to distinguish instances of the same service that exist // at the same time (e.g. instances of a horizontally scaled service). It is // preferable for the ID to be persistent and stay the same for the lifetime of // the service instance, however it is acceptable that the ID is ephemeral and // changes during important lifetime events for the service (e.g. service // restarts). If the service has no inherent unique ID that can be used as the // value of this attribute it is recommended to generate a random Version 1 or // Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use // Version 5, see RFC 4122 for more recommendations). ServiceInstanceIDKey = attribute.Key("service.instance.id") // The version string of the service API or implementation. // // Type: string // Required: No // Stability: stable // Examples: '2.0.0' ServiceVersionKey = attribute.Key("service.version") ) // The telemetry SDK used to capture data recorded by the instrumentation libraries. const ( // The name of the telemetry SDK as defined above. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") // The language of the telemetry SDK. // // Type: Enum // Required: No // Stability: stable TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") // The version string of the telemetry SDK. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") // The version string of the auto instrumentation agent, if used. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") ) var ( // cpp TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") // dotnet TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") // erlang TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") // go TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") // java TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") // nodejs TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") // php TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") // python TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") // ruby TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") ) // Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. const ( // The name of the web engine. // // Type: string // Required: Always // Stability: stable // Examples: 'WildFly' WebEngineNameKey = attribute.Key("webengine.name") // The version of the web engine. // // Type: string // Required: No // Stability: stable // Examples: '21.0.0' WebEngineVersionKey = attribute.Key("webengine.version") // Additional description of the web engine (e.g. detailed version and edition // information). // // Type: string // Required: No // Stability: stable // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final' WebEngineDescriptionKey = attribute.Key("webengine.description") ) golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/schema.go000066400000000000000000000017131414226744000227130ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ const SchemaURL = "https://opentelemetry.io/schemas/v1.6.1" golang-opentelemetry-otel-1.1.0/semconv/v1.6.1/trace.go000066400000000000000000001502051414226744000225520ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.6.1" import "go.opentelemetry.io/otel/attribute" // Span attributes used by AWS Lambda (in addition to general `faas` attributes). const ( // The full invoked ARN as provided on the `Context` passed to the function // (`Lambda-Runtime-Invoked-Function-ARN` header on the `/runtime/invocation/next` // applicable). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-east-1:123456:function:myfunction:myalias' // Note: This may be different from `faas.id` if an alias is involved. AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") ) // This document defines the attributes used to perform database client calls. const ( // An identifier for the database management system (DBMS) product being used. See // below for a list of well-known identifiers. // // Type: Enum // Required: Always // Stability: stable DBSystemKey = attribute.Key("db.system") // The connection string used to connect to the database. It is recommended to // remove embedded credentials. // // Type: string // Required: No // Stability: stable // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' DBConnectionStringKey = attribute.Key("db.connection_string") // Username for accessing the database. // // Type: string // Required: No // Stability: stable // Examples: 'readonly_user', 'reporting_user' DBUserKey = attribute.Key("db.user") // The fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver // used to connect. // // Type: string // Required: No // Stability: stable // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") // If no [tech-specific attribute](#call-level-attributes-for-specific- // technologies) is defined, this attribute is used to report the name of the // database being accessed. For commands that switch the database, this should be // set to the target database (even if the command fails). // // Type: string // Required: Required, if applicable and no more-specific attribute is defined. // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called "schema // name". DBNameKey = attribute.Key("db.name") // The database statement being executed. // // Type: string // Required: Required if applicable and not explicitly disabled via // instrumentation configuration. // Stability: stable // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' // Note: The value may be sanitized to exclude sensitive information. DBStatementKey = attribute.Key("db.statement") // The name of the operation being executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. // // Type: string // Required: Required, if `db.statement` is not applicable. // Stability: stable // Examples: 'findAndModify', 'HMSET', 'SELECT' // Note: When setting this to an SQL keyword, it is not recommended to attempt any // client-side parsing of `db.statement` just to get this property, but it should // be set if the operation name is provided by the library being instrumented. If // the SQL statement has an ambiguous operation, or performs more than one // operation, this value may be omitted. DBOperationKey = attribute.Key("db.operation") ) var ( // Some other SQL database. Fallback only. See notes DBSystemOtherSQL = DBSystemKey.String("other_sql") // Microsoft SQL Server DBSystemMSSQL = DBSystemKey.String("mssql") // MySQL DBSystemMySQL = DBSystemKey.String("mysql") // Oracle Database DBSystemOracle = DBSystemKey.String("oracle") // IBM DB2 DBSystemDB2 = DBSystemKey.String("db2") // PostgreSQL DBSystemPostgreSQL = DBSystemKey.String("postgresql") // Amazon Redshift DBSystemRedshift = DBSystemKey.String("redshift") // Apache Hive DBSystemHive = DBSystemKey.String("hive") // Cloudscape DBSystemCloudscape = DBSystemKey.String("cloudscape") // HyperSQL DataBase DBSystemHSQLDB = DBSystemKey.String("hsqldb") // Progress Database DBSystemProgress = DBSystemKey.String("progress") // SAP MaxDB DBSystemMaxDB = DBSystemKey.String("maxdb") // SAP HANA DBSystemHanaDB = DBSystemKey.String("hanadb") // Ingres DBSystemIngres = DBSystemKey.String("ingres") // FirstSQL DBSystemFirstSQL = DBSystemKey.String("firstsql") // EnterpriseDB DBSystemEDB = DBSystemKey.String("edb") // InterSystems Caché DBSystemCache = DBSystemKey.String("cache") // Adabas (Adaptable Database System) DBSystemAdabas = DBSystemKey.String("adabas") // Firebird DBSystemFirebird = DBSystemKey.String("firebird") // Apache Derby DBSystemDerby = DBSystemKey.String("derby") // FileMaker DBSystemFilemaker = DBSystemKey.String("filemaker") // Informix DBSystemInformix = DBSystemKey.String("informix") // InstantDB DBSystemInstantDB = DBSystemKey.String("instantdb") // InterBase DBSystemInterbase = DBSystemKey.String("interbase") // MariaDB DBSystemMariaDB = DBSystemKey.String("mariadb") // Netezza DBSystemNetezza = DBSystemKey.String("netezza") // Pervasive PSQL DBSystemPervasive = DBSystemKey.String("pervasive") // PointBase DBSystemPointbase = DBSystemKey.String("pointbase") // SQLite DBSystemSqlite = DBSystemKey.String("sqlite") // Sybase DBSystemSybase = DBSystemKey.String("sybase") // Teradata DBSystemTeradata = DBSystemKey.String("teradata") // Vertica DBSystemVertica = DBSystemKey.String("vertica") // H2 DBSystemH2 = DBSystemKey.String("h2") // ColdFusion IMQ DBSystemColdfusion = DBSystemKey.String("coldfusion") // Apache Cassandra DBSystemCassandra = DBSystemKey.String("cassandra") // Apache HBase DBSystemHBase = DBSystemKey.String("hbase") // MongoDB DBSystemMongoDB = DBSystemKey.String("mongodb") // Redis DBSystemRedis = DBSystemKey.String("redis") // Couchbase DBSystemCouchbase = DBSystemKey.String("couchbase") // CouchDB DBSystemCouchDB = DBSystemKey.String("couchdb") // Microsoft Azure Cosmos DB DBSystemCosmosDB = DBSystemKey.String("cosmosdb") // Amazon DynamoDB DBSystemDynamoDB = DBSystemKey.String("dynamodb") // Neo4j DBSystemNeo4j = DBSystemKey.String("neo4j") // Apache Geode DBSystemGeode = DBSystemKey.String("geode") // Elasticsearch DBSystemElasticsearch = DBSystemKey.String("elasticsearch") // Memcached DBSystemMemcached = DBSystemKey.String("memcached") // CockroachDB DBSystemCockroachdb = DBSystemKey.String("cockroachdb") ) // Connection-level attributes for Microsoft SQL Server const ( // The Microsoft SQL Server [instance name](https://docs.microsoft.com/en- // us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named instance. // // Type: string // Required: No // Stability: stable // Examples: 'MSSQLSERVER' // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer // required (but still recommended if non-standard). DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") ) // Call-level attributes for Cassandra const ( // The name of the keyspace being accessed. To be used instead of the generic // `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'mykeyspace' DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") // The fetch size used for paging, i.e. how many rows will be returned at once. // // Type: int // Required: No // Stability: stable // Examples: 5000 DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") // The consistency level of the query. Based on consistency values from // [CQL](https://docs.datastax.com/en/cassandra- // oss/3.0/cassandra/dml/dmlConfigConsistency.html). // // Type: Enum // Required: No // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'mytable' // Note: This mirrors the db.sql.table attribute but references cassandra rather // than sql. It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBCassandraTableKey = attribute.Key("db.cassandra.table") // Whether or not the query is idempotent. // // Type: boolean // Required: No // Stability: stable DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") // The number of times a query was speculatively executed. Not set or `0` if the // query was not executed speculatively. // // Type: int // Required: No // Stability: stable // Examples: 0, 2 DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") // The ID of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") // The data center of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'us-west-2' DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") ) var ( // all DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") // each_quorum DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") // quorum DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") // local_quorum DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") // one DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") // two DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") // three DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") // local_one DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") // any DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") // serial DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") // local_serial DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) // Call-level attributes for Apache HBase const ( // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being // accessed. To be used instead of the generic `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'default' DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") ) // Call-level attributes for Redis const ( // The index of the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To be used // instead of the generic `db.name` attribute. // // Type: int // Required: Required, if other than the default database (`0`). // Stability: stable // Examples: 0, 1, 15 DBRedisDBIndexKey = attribute.Key("db.redis.database_index") ) // Call-level attributes for MongoDB const ( // The collection being accessed within the database stated in `db.name`. // // Type: string // Required: Always // Stability: stable // Examples: 'customers', 'products' DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) // Call-level attrbiutes for SQL databases const ( // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'public.users', 'customers' // Note: It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBSQLTableKey = attribute.Key("db.sql.table") ) // This document defines the attributes used to report a single exception associated with a span. const ( // The type of the exception (its fully-qualified class name, if applicable). The // dynamic type of the exception should be preferred over the static type in // languages that support it. // // Type: string // Required: No // Stability: stable // Examples: 'java.net.ConnectException', 'OSError' ExceptionTypeKey = attribute.Key("exception.type") // The exception message. // // Type: string // Required: No // Stability: stable // Examples: 'Division by zero', "Can't convert 'int' object to str implicitly" ExceptionMessageKey = attribute.Key("exception.message") // A stacktrace as a string in the natural representation for the language // runtime. The representation is to be determined and documented by each language // SIG. // // Type: string // Required: No // Stability: stable // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test // exception\\n at ' // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' ExceptionStacktraceKey = attribute.Key("exception.stacktrace") // SHOULD be set to true if the exception event is recorded at a point where it is // known that the exception is escaping the scope of the span. // // Type: boolean // Required: No // Stability: stable // Note: An exception is considered to have escaped (or left) the scope of a span, // if that span is ended while the exception is still logically "in flight". // This may be actually "in flight" in some languages (e.g. if the exception // is passed to a Context manager's `__exit__` method in Python) but will // usually be caught at the point of recording the exception in most languages. // It is usually not possible to determine at the point where an exception is // thrown // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending the span, // as done in the [example above](#exception-end-example). // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, // since the event might have been recorded at a time where it was not // clear whether the exception will escape. ExceptionEscapedKey = attribute.Key("exception.escaped") ) // This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. const ( // Type of the trigger on which the function is executed. // // Type: Enum // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing // invocations, if it is known to the client. This is, for example, not the case, // when the transport layer is abstracted in a FaaS client framework without // access to its configuration. // Stability: stable FaaSTriggerKey = attribute.Key("faas.trigger") // The execution ID of the current function execution. // // Type: string // Required: No // Stability: stable // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' FaaSExecutionKey = attribute.Key("faas.execution") ) var ( // A response to some data source operation such as a database or filesystem read/write FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") // To provide an answer to an inbound HTTP request FaaSTriggerHTTP = FaaSTriggerKey.String("http") // A function is set to be executed when messages are sent to a messaging system FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") // A function is scheduled to be executed regularly FaaSTriggerTimer = FaaSTriggerKey.String("timer") // If none of the others apply FaaSTriggerOther = FaaSTriggerKey.String("other") ) // Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. const ( // The name of the source on which the triggering operation was performed. For // example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos // DB to the database name. // // Type: string // Required: Always // Stability: stable // Examples: 'myBucketName', 'myDBName' FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") // Describes the type of the operation that was performed on the data. // // Type: Enum // Required: Always // Stability: stable FaaSDocumentOperationKey = attribute.Key("faas.document.operation") // A string containing the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSDocumentTimeKey = attribute.Key("faas.document.time") // The document name/table subjected to the operation. For example, in Cloud // Storage or S3 is the name of the file, and in Cosmos DB the table name. // // Type: string // Required: No // Stability: stable // Examples: 'myFile.txt', 'myTableName' FaaSDocumentNameKey = attribute.Key("faas.document.name") ) var ( // When a new object is created FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") // When an object is modified FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") // When an object is deleted FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") ) // Semantic Convention for FaaS scheduled to be executed regularly. const ( // A string containing the function invocation time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSTimeKey = attribute.Key("faas.time") // A string containing the schedule period as [Cron Expression](https://docs.oracl // e.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). // // Type: string // Required: No // Stability: stable // Examples: '0/5 * * * ? *' FaaSCronKey = attribute.Key("faas.cron") ) // Contains additional attributes for incoming FaaS spans. const ( // A boolean that is true if the serverless function is executed for the first // time (aka cold-start). // // Type: boolean // Required: No // Stability: stable FaaSColdstartKey = attribute.Key("faas.coldstart") ) // Contains additional attributes for outgoing FaaS spans. const ( // The name of the invoked function. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked // function. FaaSInvokedNameKey = attribute.Key("faas.invoked_name") // The cloud provider of the invoked function. // // Type: Enum // Required: Always // Stability: stable // Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked // function. FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") // The cloud region of the invoked function. // // Type: string // Required: For some cloud providers, like AWS or GCP, the region in which a // function is hosted is essential to uniquely identify the function and also part // of its endpoint. Since it's part of the endpoint being called, the region is // always known to clients. In these cases, `faas.invoked_region` MUST be set // accordingly. If the region is unknown to the client or not required for // identifying the invoked function, setting `faas.invoked_region` is optional. // Stability: stable // Examples: 'eu-central-1' // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked // function. FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") ) var ( // Alibaba Cloud FaaSInvokedProviderAlibabaCloud = FaaSInvokedProviderKey.String("alibaba_cloud") // Amazon Web Services FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") // Microsoft Azure FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") ) // These attributes may be used for any network related operation. const ( // Transport protocol used. See note below. // // Type: Enum // Required: No // Stability: stable NetTransportKey = attribute.Key("net.transport") // Remote address of the peer (dotted decimal for IPv4 or // [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6) // // Type: string // Required: No // Stability: stable // Examples: '127.0.0.1' NetPeerIPKey = attribute.Key("net.peer.ip") // Remote port number. // // Type: int // Required: No // Stability: stable // Examples: 80, 8080, 443 NetPeerPortKey = attribute.Key("net.peer.port") // Remote hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'example.com' NetPeerNameKey = attribute.Key("net.peer.name") // Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. // // Type: string // Required: No // Stability: stable // Examples: '192.168.0.1' NetHostIPKey = attribute.Key("net.host.ip") // Like `net.peer.port` but for the host port. // // Type: int // Required: No // Stability: stable // Examples: 35555 NetHostPortKey = attribute.Key("net.host.port") // Local hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'localhost' NetHostNameKey = attribute.Key("net.host.name") // The internet connection type currently being used by the host. // // Type: Enum // Required: No // Stability: stable // Examples: 'wifi' NetHostConnectionTypeKey = attribute.Key("net.host.connection.type") // This describes more details regarding the connection.type. It may be the type // of cell technology connection, but it could be used for describing details // about a wifi connection. // // Type: Enum // Required: No // Stability: stable // Examples: 'LTE' NetHostConnectionSubtypeKey = attribute.Key("net.host.connection.subtype") // The name of the mobile carrier. // // Type: string // Required: No // Stability: stable // Examples: 'sprint' NetHostCarrierNameKey = attribute.Key("net.host.carrier.name") // The mobile carrier country code. // // Type: string // Required: No // Stability: stable // Examples: '310' NetHostCarrierMccKey = attribute.Key("net.host.carrier.mcc") // The mobile carrier network code. // // Type: string // Required: No // Stability: stable // Examples: '001' NetHostCarrierMncKey = attribute.Key("net.host.carrier.mnc") // The ISO 3166-1 alpha-2 2-character country code associated with the mobile // carrier network. // // Type: string // Required: No // Stability: stable // Examples: 'DE' NetHostCarrierIccKey = attribute.Key("net.host.carrier.icc") ) var ( // ip_tcp NetTransportTCP = NetTransportKey.String("ip_tcp") // ip_udp NetTransportUDP = NetTransportKey.String("ip_udp") // Another IP-based protocol NetTransportIP = NetTransportKey.String("ip") // Unix Domain socket. See below NetTransportUnix = NetTransportKey.String("unix") // Named or anonymous pipe. See note below NetTransportPipe = NetTransportKey.String("pipe") // In-process communication NetTransportInProc = NetTransportKey.String("inproc") // Something else (non IP-based) NetTransportOther = NetTransportKey.String("other") ) var ( // wifi NetHostConnectionTypeWifi = NetHostConnectionTypeKey.String("wifi") // wired NetHostConnectionTypeWired = NetHostConnectionTypeKey.String("wired") // cell NetHostConnectionTypeCell = NetHostConnectionTypeKey.String("cell") // unavailable NetHostConnectionTypeUnavailable = NetHostConnectionTypeKey.String("unavailable") // unknown NetHostConnectionTypeUnknown = NetHostConnectionTypeKey.String("unknown") ) var ( // GPRS NetHostConnectionSubtypeGprs = NetHostConnectionSubtypeKey.String("gprs") // EDGE NetHostConnectionSubtypeEdge = NetHostConnectionSubtypeKey.String("edge") // UMTS NetHostConnectionSubtypeUmts = NetHostConnectionSubtypeKey.String("umts") // CDMA NetHostConnectionSubtypeCdma = NetHostConnectionSubtypeKey.String("cdma") // EVDO Rel. 0 NetHostConnectionSubtypeEvdo0 = NetHostConnectionSubtypeKey.String("evdo_0") // EVDO Rev. A NetHostConnectionSubtypeEvdoA = NetHostConnectionSubtypeKey.String("evdo_a") // CDMA2000 1XRTT NetHostConnectionSubtypeCdma20001xrtt = NetHostConnectionSubtypeKey.String("cdma2000_1xrtt") // HSDPA NetHostConnectionSubtypeHsdpa = NetHostConnectionSubtypeKey.String("hsdpa") // HSUPA NetHostConnectionSubtypeHsupa = NetHostConnectionSubtypeKey.String("hsupa") // HSPA NetHostConnectionSubtypeHspa = NetHostConnectionSubtypeKey.String("hspa") // IDEN NetHostConnectionSubtypeIden = NetHostConnectionSubtypeKey.String("iden") // EVDO Rev. B NetHostConnectionSubtypeEvdoB = NetHostConnectionSubtypeKey.String("evdo_b") // LTE NetHostConnectionSubtypeLte = NetHostConnectionSubtypeKey.String("lte") // EHRPD NetHostConnectionSubtypeEhrpd = NetHostConnectionSubtypeKey.String("ehrpd") // HSPAP NetHostConnectionSubtypeHspap = NetHostConnectionSubtypeKey.String("hspap") // GSM NetHostConnectionSubtypeGsm = NetHostConnectionSubtypeKey.String("gsm") // TD-SCDMA NetHostConnectionSubtypeTdScdma = NetHostConnectionSubtypeKey.String("td_scdma") // IWLAN NetHostConnectionSubtypeIwlan = NetHostConnectionSubtypeKey.String("iwlan") // 5G NR (New Radio) NetHostConnectionSubtypeNr = NetHostConnectionSubtypeKey.String("nr") // 5G NRNSA (New Radio Non-Standalone) NetHostConnectionSubtypeNrnsa = NetHostConnectionSubtypeKey.String("nrnsa") // LTE CA NetHostConnectionSubtypeLteCa = NetHostConnectionSubtypeKey.String("lte_ca") ) // Operations that access some remote service. const ( // The [`service.name`](../../resource/semantic_conventions/README.md#service) of // the remote service. SHOULD be equal to the actual `service.name` resource // attribute of the remote service if any. // // Type: string // Required: No // Stability: stable // Examples: 'AuthTokenCache' PeerServiceKey = attribute.Key("peer.service") ) // These attributes may be used for any operation with an authenticated and/or authorized enduser. const ( // Username or client_id extracted from the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the // inbound request from outside the system. // // Type: string // Required: No // Stability: stable // Examples: 'username' EnduserIDKey = attribute.Key("enduser.id") // Actual/assumed role the client is making the request under extracted from token // or application security context. // // Type: string // Required: No // Stability: stable // Examples: 'admin' EnduserRoleKey = attribute.Key("enduser.role") // Scopes or granted authorities the client currently possesses extracted from // token or application security context. The value would come from the scope // associated with an [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value // in a [SAML 2.0 Assertion](http://docs.oasis- // open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). // // Type: string // Required: No // Stability: stable // Examples: 'read:message, write:files' EnduserScopeKey = attribute.Key("enduser.scope") ) // These attributes may be used for any operation to store information about a thread that started a span. const ( // Current "managed" thread ID (as opposed to OS thread ID). // // Type: int // Required: No // Stability: stable // Examples: 42 ThreadIDKey = attribute.Key("thread.id") // Current thread name. // // Type: string // Required: No // Stability: stable // Examples: 'main' ThreadNameKey = attribute.Key("thread.name") ) // These attributes allow to report this unit of code and therefore to provide more context about the span. const ( // The method or function name, or equivalent (usually rightmost part of the code // unit's name). // // Type: string // Required: No // Stability: stable // Examples: 'serveRequest' CodeFunctionKey = attribute.Key("code.function") // The "namespace" within which `code.function` is defined. Usually the qualified // class or module name, such that `code.namespace` + some separator + // `code.function` form a unique identifier for the code unit. // // Type: string // Required: No // Stability: stable // Examples: 'com.example.MyHTTPService' CodeNamespaceKey = attribute.Key("code.namespace") // The source code file name that identifies the code unit as uniquely as possible // (preferably an absolute file path). // // Type: string // Required: No // Stability: stable // Examples: '/usr/local/MyApplication/content_root/app/index.php' CodeFilepathKey = attribute.Key("code.filepath") // The line number in `code.filepath` best representing the operation. It SHOULD // point within the code unit named in `code.function`. // // Type: int // Required: No // Stability: stable // Examples: 42 CodeLineNumberKey = attribute.Key("code.lineno") ) // This document defines semantic conventions for HTTP client and server Spans. const ( // HTTP request method. // // Type: string // Required: Always // Stability: stable // Examples: 'GET', 'POST', 'HEAD' HTTPMethodKey = attribute.Key("http.method") // Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. // Usually the fragment is not transmitted over HTTP, but if it is known, it // should be included nevertheless. // // Type: string // Required: No // Stability: stable // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' // Note: `http.url` MUST NOT contain credentials passed via URL in form of // `https://username:password@www.example.com/`. In such case the attribute's // value should be `https://www.example.com/`. HTTPURLKey = attribute.Key("http.url") // The full request target as passed in a HTTP request line or equivalent. // // Type: string // Required: No // Stability: stable // Examples: '/path/12314/?q=ddds#123' HTTPTargetKey = attribute.Key("http.target") // The value of the [HTTP host // header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is // empty or not present, this attribute should be the same. // // Type: string // Required: No // Stability: stable // Examples: 'www.example.org' HTTPHostKey = attribute.Key("http.host") // The URI scheme identifying the used protocol. // // Type: string // Required: No // Stability: stable // Examples: 'http', 'https' HTTPSchemeKey = attribute.Key("http.scheme") // [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). // // Type: int // Required: If and only if one was received/sent. // Stability: stable // Examples: 200 HTTPStatusCodeKey = attribute.Key("http.status_code") // Kind of HTTP protocol used. // // Type: Enum // Required: No // Stability: stable // Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` // except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. HTTPFlavorKey = attribute.Key("http.flavor") // Value of the [HTTP User- // Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the // client. // // Type: string // Required: No // Stability: stable // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' HTTPUserAgentKey = attribute.Key("http.user_agent") // The size of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") // The size of the uncompressed request payload body after transport decoding. Not // set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPRequestContentLengthUncompressedKey = attribute.Key("http.request_content_length_uncompressed") // The size of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") // The size of the uncompressed response payload body after transport decoding. // Not set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") ) var ( // HTTP 1.0 HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") // HTTP 1.1 HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") // HTTP 2 HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") // SPDY protocol HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") // QUIC protocol HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") ) // Semantic Convention for HTTP Server const ( // The primary server name of the matched virtual host. This should be obtained // via configuration. If no such configuration can be obtained, this attribute // MUST NOT be set ( `net.host.name` should be used instead). // // Type: string // Required: No // Stability: stable // Examples: 'example.com' // Note: `http.url` is usually not readily available on the server side but would // have to be assembled in a cumbersome and sometimes lossy process from other // information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus // preferred to supply the raw data that is available. HTTPServerNameKey = attribute.Key("http.server_name") // The matched route (path template). // // Type: string // Required: No // Stability: stable // Examples: '/users/:userID?' HTTPRouteKey = attribute.Key("http.route") // The IP address of the original client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en- // US/docs/Web/HTTP/Headers/X-Forwarded-For)). // // Type: string // Required: No // Stability: stable // Examples: '83.164.160.102' // Note: This is not necessarily the same as `net.peer.ip`, which would identify // the network-level peer, which may be a proxy. HTTPClientIPKey = attribute.Key("http.client_ip") ) // Attributes that exist for multiple DynamoDB request types. const ( // The keys in the `RequestItems` object field. // // Type: string[] // Required: No // Stability: stable // Examples: 'Users', 'Cats' AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") // The JSON-serialized value of each item in the `ConsumedCapacity` response // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : { // "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": // "string", "WriteCapacityUnits": number }' AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") // The JSON-serialized value of the `ItemCollectionMetrics` response field. // // Type: string // Required: No // Stability: stable // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }' AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") // The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") // The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") // The value of the `ConsistentRead` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") // The value of the `ProjectionExpression` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Title', 'Title, Price, Color', 'Title, Description, RelatedItems, // ProductReviews' AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") // The value of the `Limit` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") // The value of the `AttributesToGet` request parameter. // // Type: string[] // Required: No // Stability: stable // Examples: 'lives', 'id' AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") // The value of the `IndexName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'name_to_group' AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") // The value of the `Select` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'ALL_ATTRIBUTES', 'COUNT' AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") ) // DynamoDB.CreateTable const ( // The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request // field // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" }, "ProvisionedThroughput": { "ReadCapacityUnits": // number, "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") // The JSON-serialized value of each item of the `LocalSecondaryIndexes` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexARN": "string", "IndexName": "string", "IndexSizeBytes": // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" } }' AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") ) // DynamoDB.ListTables const ( // The value of the `ExclusiveStartTableName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Users', 'CatsTable' AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") // The the number of items in the `TableNames` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 20 AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") ) // DynamoDB.Query const ( // The value of the `ScanIndexForward` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") ) // DynamoDB.Scan const ( // The value of the `Segment` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") // The value of the `TotalSegments` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 100 AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") // The value of the `Count` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") // The value of the `ScannedCount` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 50 AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") ) // DynamoDB.UpdateTable const ( // The JSON-serialized value of each item in the `AttributeDefinitions` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") // The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` // request field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }' AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") ) // This document defines the attributes used in messaging systems. const ( // A string identifying the messaging system. // // Type: string // Required: Always // Stability: stable // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // The message destination name. This might be equal to the span name but is // required nevertheless. // // Type: string // Required: Always // Stability: stable // Examples: 'MyQueue', 'MyTopic' MessagingDestinationKey = attribute.Key("messaging.destination") // The kind of message destination // // Type: Enum // Required: Required only if the message destination is either a `queue` or // `topic`. // Stability: stable MessagingDestinationKindKey = attribute.Key("messaging.destination_kind") // A boolean that is true if the message destination is temporary. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingTempDestinationKey = attribute.Key("messaging.temp_destination") // The name of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: 'AMQP', 'MQTT' MessagingProtocolKey = attribute.Key("messaging.protocol") // The version of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: '0.9.1' MessagingProtocolVersionKey = attribute.Key("messaging.protocol_version") // Connection string. // // Type: string // Required: No // Stability: stable // Examples: 'tibjmsnaming://localhost:7222', // 'https://queue.amazonaws.com/80398EXAMPLE/MyQueue' MessagingURLKey = attribute.Key("messaging.url") // A value used by the messaging system as an identifier for the message, // represented as a string. // // Type: string // Required: No // Stability: stable // Examples: '452a7c7c7c7048c2f887f61572b18fc2' MessagingMessageIDKey = attribute.Key("messaging.message_id") // The [conversation ID](#conversations) identifying the conversation to which the // message belongs, represented as a string. Sometimes called "Correlation ID". // // Type: string // Required: No // Stability: stable // Examples: 'MyConversationID' MessagingConversationIDKey = attribute.Key("messaging.conversation_id") // The (uncompressed) size of the message payload in bytes. Also use this // attribute if it is unknown whether the compressed or uncompressed payload size // is reported. // // Type: int // Required: No // Stability: stable // Examples: 2738 MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message_payload_size_bytes") // The compressed size of the message payload in bytes. // // Type: int // Required: No // Stability: stable // Examples: 2048 MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message_payload_compressed_size_bytes") ) var ( // A message sent to a queue MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") // A message sent to a topic MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") ) // Semantic convention for a consumer of messages received from a messaging system const ( // A string identifying the kind of message consumption as defined in the // [Operation names](#operation-names) section above. If the operation is "send", // this attribute MUST NOT be set, since the operation can be inferred from the // span kind in that case. // // Type: Enum // Required: No // Stability: stable MessagingOperationKey = attribute.Key("messaging.operation") ) var ( // receive MessagingOperationReceive = MessagingOperationKey.String("receive") // process MessagingOperationProcess = MessagingOperationKey.String("process") ) // Attributes for RabbitMQ const ( // RabbitMQ message routing key. // // Type: string // Required: Unless it is empty. // Stability: stable // Examples: 'myKey' MessagingRabbitmqRoutingKeyKey = attribute.Key("messaging.rabbitmq.routing_key") ) // Attributes for Apache Kafka const ( // Message keys in Kafka are used for grouping alike messages to ensure they're // processed on the same partition. They differ from `messaging.message_id` in // that they're not unique. If the key is `null`, the attribute MUST NOT be set. // // Type: string // Required: No // Stability: stable // Examples: 'myKey' // Note: If the key type is not string, it's string representation has to be // supplied for the attribute. If the key has no unambiguous, canonical string // form, don't include its value. MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message_key") // Name of the Kafka Consumer Group that is handling the message. Only applies to // consumers, not producers. // // Type: string // Required: No // Stability: stable // Examples: 'my-group' MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer_group") // Client ID for the Consumer or Producer that is handling the message. // // Type: string // Required: No // Stability: stable // Examples: 'client-5' MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") // Partition the message is sent to. // // Type: int // Required: No // Stability: stable // Examples: 2 MessagingKafkaPartitionKey = attribute.Key("messaging.kafka.partition") // A boolean that is true if the message is a tombstone. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") ) // This document defines semantic conventions for remote procedure calls. const ( // A string identifying the remoting system. // // Type: string // Required: Always // Stability: stable // Examples: 'grpc', 'java_rmi', 'wcf' RPCSystemKey = attribute.Key("rpc.system") // The full (logical) name of the service being called, including its package // name, if applicable. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'myservice.EchoService' // Note: This is the logical name of the service from the RPC interface // perspective, which can be different from the name of any implementing class. // The `code.namespace` attribute may be used to store the latter (despite the // attribute name, it may include a class name; e.g., class with method actually // executing the call on the server side, RPC client stub class on the client // side). RPCServiceKey = attribute.Key("rpc.service") // The name of the (logical) method being called, must be equal to the $method // part in the span name. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'exampleMethod' // Note: This is the logical name of the method from the RPC interface // perspective, which can be different from the name of any implementing // method/function. The `code.function` attribute may be used to store the latter // (e.g., method actually executing the call on the server side, RPC client stub // method on the client side). RPCMethodKey = attribute.Key("rpc.method") ) // Tech-specific attributes for gRPC. const ( // The [numeric status // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC // request. // // Type: Enum // Required: Always // Stability: stable RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) var ( // OK RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) // CANCELLED RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) // UNKNOWN RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) // INVALID_ARGUMENT RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) // DEADLINE_EXCEEDED RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) // NOT_FOUND RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) // ALREADY_EXISTS RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) // PERMISSION_DENIED RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) // RESOURCE_EXHAUSTED RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) // FAILED_PRECONDITION RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) // ABORTED RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) // OUT_OF_RANGE RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) // UNIMPLEMENTED RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) // INTERNAL RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) // UNAVAILABLE RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) // DATA_LOSS RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) // UNAUTHENTICATED RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) ) // Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). const ( // Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC // 1.0 does not specify this, the value can be omitted. // // Type: string // Required: If missing, it is assumed to be "1.0". // Stability: stable // Examples: '2.0', '1.0' RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") // `id` property of request or response. Since protocol allows id to be int, // string, `null` or missing (for notifications), value is expected to be cast to // string for simplicity. Use empty string in case of `null` value. Omit entirely // if this is a notification. // // Type: string // Required: No // Stability: stable // Examples: '10', 'request-7', '' RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") // `error.code` property of response if it is an error response. // // Type: int // Required: If missing, response is assumed to be successful. // Stability: stable // Examples: -32700, 100 RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") // `error.message` property of response if it is an error response. // // Type: string // Required: No // Stability: stable // Examples: 'Parse error', 'User already exists' RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") ) golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/000077500000000000000000000000001414226744000211225ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/doc.go000066400000000000000000000016621414226744000222230ustar00rootroot00000000000000// 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 semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions // as of the v1.7.0 version of the OpenTelemetry specification. package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/exception.go000066400000000000000000000014271414226744000234530ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" const ( // ExceptionEventName is the name of the Span event representing an exception. ExceptionEventName = "exception" ) golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/http.go000066400000000000000000000217301414226744000224330ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" import ( "fmt" "net" "net/http" "strconv" "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // HTTP scheme attributes. var ( HTTPSchemeHTTP = HTTPSchemeKey.String("http") HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} switch network { case "tcp", "tcp4", "tcp6": attrs = append(attrs, NetTransportTCP) case "udp", "udp4", "udp6": attrs = append(attrs, NetTransportUDP) case "ip", "ip4", "ip6": attrs = append(attrs, NetTransportIP) case "unix", "unixgram", "unixpacket": attrs = append(attrs, NetTransportUnix) default: attrs = append(attrs, NetTransportOther) } peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) if peerIP != "" { attrs = append(attrs, NetPeerIPKey.String(peerIP)) } if peerName != "" { attrs = append(attrs, NetPeerNameKey.String(peerName)) } if peerPort != 0 { attrs = append(attrs, NetPeerPortKey.Int(peerPort)) } hostIP, hostName, hostPort := "", "", 0 for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { hostIP, hostName, hostPort = hostIPNamePort(someHost) if hostIP != "" || hostName != "" || hostPort != 0 { break } } if hostIP != "" { attrs = append(attrs, NetHostIPKey.String(hostIP)) } if hostName != "" { attrs = append(attrs, NetHostNameKey.String(hostName)) } if hostPort != 0 { attrs = append(attrs, NetHostPortKey.Int(hostPort)) } return attrs } // hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. // It handles both IPv4 and IPv6 addresses. If the host portion is not recognized // as a valid IPv4 or IPv6 address, the `ip` result will be empty and the // host portion will instead be returned in `name`. func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { var ( hostPart, portPart string parsedPort uint64 err error ) if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { hostPart, portPart = hostWithPort, "" } if parsedIP := net.ParseIP(hostPart); parsedIP != nil { ip = parsedIP.String() } else { name = hostPart } if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { port = int(parsedPort) } return } // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { if username, _, ok := request.BasicAuth(); ok { return []attribute.KeyValue{EnduserIDKey.String(username)} } return nil } // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if request.Method != "" { attrs = append(attrs, HTTPMethodKey.String(request.Method)) } else { attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) } // remove any username/password info that may be in the URL // before adding it to the attributes userinfo := request.URL.User request.URL.User = nil attrs = append(attrs, HTTPURLKey.String(request.URL.String())) // restore any username/password info that was removed request.URL.User = userinfo return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if ua := request.UserAgent(); ua != "" { attrs = append(attrs, HTTPUserAgentKey.String(ua)) } if request.ContentLength > 0 { attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality attrs := []attribute.KeyValue{} if request.TLS != nil { attrs = append(attrs, HTTPSchemeHTTPS) } else { attrs = append(attrs, HTTPSchemeHTTP) } if request.Host != "" { attrs = append(attrs, HTTPHostKey.String(request.Host)) } flavor := "" if request.ProtoMajor == 1 { flavor = fmt.Sprintf("1.%d", request.ProtoMinor) } else if request.ProtoMajor == 2 { flavor = "2" } if flavor != "" { attrs = append(attrs, HTTPFlavorKey.String(flavor)) } return attrs } // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) } // HTTPServerAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the server side. Currently, only basic authentication is // supported. func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPMethodKey.String(request.Method), HTTPTargetKey.String(request.RequestURI), } if serverName != "" { attrs = append(attrs, HTTPServerNameKey.String(serverName)) } if route != "" { attrs = append(attrs, HTTPRouteKey.String(route)) } if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) } } return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { attrs := []attribute.KeyValue{ HTTPStatusCodeKey.Int(code), } return attrs } type codeRange struct { fromInclusive int toInclusive int } func (r codeRange) contains(code int) bool { return r.fromInclusive <= code && code <= r.toInclusive } var validRangesPerCategory = map[int][]codeRange{ 1: { {http.StatusContinue, http.StatusEarlyHints}, }, 2: { {http.StatusOK, http.StatusAlreadyReported}, {http.StatusIMUsed, http.StatusIMUsed}, }, 3: { {http.StatusMultipleChoices, http.StatusUseProxy}, {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, }, 4: { {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, {http.StatusPreconditionRequired, http.StatusTooManyRequests}, {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, }, 5: { {http.StatusInternalServerError, http.StatusLoopDetected}, {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, }, } // SpanStatusFromHTTPStatusCode generates a status code and a message // as specified by the OpenTelemetry specification for a span. func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { spanCode, valid := validateHTTPStatusCode(code) if !valid { return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) } return spanCode, "" } // Validates the HTTP status code and returns corresponding span status code. // If the `code` is not a valid HTTP status code, returns span status Error // and false. func validateHTTPStatusCode(code int) (codes.Code, bool) { category := code / 100 ranges, ok := validRangesPerCategory[category] if !ok { return codes.Error, false } ok = false for _, crange := range ranges { ok = crange.contains(code) if ok { break } } if !ok { return codes.Error, false } if category > 0 && category < 4 { return codes.Unset, true } return codes.Error, true } golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/http_test.go000066400000000000000000000725421414226744000235010ustar00rootroot00000000000000// 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 semconv import ( "crypto/tls" "net/http" "net/url" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) type tlsOption int const ( noTLS tlsOption = iota withTLS ) func TestNetAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string network string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped, tcp", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), }, }, { name: "stripped, udp", network: "udp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_udp"), }, }, { name: "stripped, ip", network: "ip", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip"), }, }, { name: "stripped, unix", network: "unix", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "unix"), }, }, { name: "stripped, other", network: "nih", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "other"), }, }, { name: "with remote ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[fe80::0202:b3ff:fe1e:8329]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4-in-v6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "[::ffff:192.168.0.1]:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com:56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), attribute.Int("net.peer.port", 56), }, }, { name: "with remote ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), }, }, { name: "with remote ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "fe80::0202:b3ff:fe1e:8329", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with remote ipv4_in_v6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "::ffff:192.168.0.1", // section 2.5.5.2 of RFC4291 host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "192.168.0.1"), }, }, { name: "with remote name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "example.com", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.name", "example.com"), }, }, { name: "with remote port only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: ":56", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.Int("net.peer.port", 56), }, }, { name: "with host name only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 only", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "fe80::0202:b3ff:fe1e:8329", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with host name and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:78", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, { name: "with host name and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "example.com:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.name", "example.com"), }, }, { name: "with host ipv4 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "4.3.2.1:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), }, }, { name: "with host ipv6 and bogus port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "[fe80::202:b3ff:fe1e:8329]:qwerty", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), }, }, { name: "with empty host and port", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: ":80", url: &url.URL{ Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.Int("net.host.port", 80), }, }, { name: "with host ip and port in headers", network: "tcp", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "Host": []string{"4.3.2.1:78"}, }, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv4 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "4.3.2.1:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "4.3.2.1"), attribute.Int("net.host.port", 78), }, }, { name: "with host ipv6 and port in url", network: "tcp", method: "GET", requestURI: "http://4.3.2.1:78/user/123", proto: "HTTP/1.0", remoteAddr: "1.2.3.4:56", host: "", url: &url.URL{ Host: "[fe80::202:b3ff:fe1e:8329]:78", Path: "/user/123", }, header: nil, expected: []attribute.KeyValue{ attribute.String("net.transport", "ip_tcp"), attribute.String("net.peer.ip", "1.2.3.4"), attribute.Int("net.peer.port", 56), attribute.String("net.host.ip", "fe80::202:b3ff:fe1e:8329"), attribute.Int("net.host.port", 78), }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS) got := NetAttributesFromHTTPRequest(tc.network, r) if diff := cmp.Diff( tc.expected, got, cmp.AllowUnexported(attribute.Value{})); diff != "" { t.Fatalf("attributes differ: diff %+v,", diff) } }) } } func TestEndUserAttributesFromHTTPRequest(t *testing.T) { r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS) var expected []attribute.KeyValue got := EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) r.SetBasicAuth("admin", "password") expected = []attribute.KeyValue{attribute.String("enduser.id", "admin")} got = EndUserAttributesFromHTTPRequest(r) assert.ElementsMatch(t, expected, got) } func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) { type testcase struct { name string serverName string route string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue } testcases := []testcase{ { name: "stripped", serverName: "", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with server name", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with tls", serverName: "my-server-name", route: "", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), }, }, { name: "with route", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), }, }, { name: "with host", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with proxy info", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"203.0.113.195, 70.41.3.18, 150.172.238.178"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "203.0.113.195"), }, }, { name: "with http 1.1", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with http 2", serverName: "my-server-name", route: "/user/:id", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, "X-Forwarded-For": []string{"1.2.3.4"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.server_name", "my-server-name"), attribute.String("http.route", "/user/:id"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), attribute.String("http.client_ip", "1.2.3.4"), }, }, { name: "with content length", method: "GET", requestURI: "/user/123", contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.target", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, } for idx, tc := range testcases { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r) assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name) } } func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) { expected := []attribute.KeyValue{ attribute.Int("http.status_code", 404), } got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound) assertElementsMatch(t, expected, got, "with valid HTTP status code") assert.ElementsMatch(t, expected, got) expected = []attribute.KeyValue{ attribute.Int("http.status_code", 499), } got = HTTPAttributesFromHTTPStatusCode(499) assertElementsMatch(t, expected, got, "with invalid HTTP status code") } func TestSpanStatusFromHTTPStatusCode(t *testing.T) { for code := 0; code < 1000; code++ { expected := getExpectedCodeForHTTPCode(code) got, msg := SpanStatusFromHTTPStatusCode(code) assert.Equalf(t, expected, got, "%s vs %s", expected, got) _, valid := validateHTTPStatusCode(code) if !valid { assert.NotEmpty(t, msg, "message should be set if error cannot be inferred from code") } else { assert.Empty(t, msg, "message should not be set if error can be inferred from code") } } } func getExpectedCodeForHTTPCode(code int) codes.Code { if http.StatusText(code) == "" { return codes.Error } switch code { case http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusTooManyRequests, http.StatusNotImplemented, http.StatusServiceUnavailable, http.StatusGatewayTimeout: return codes.Error } category := code / 100 if category > 0 && category < 4 { return codes.Unset } return codes.Error } func assertElementsMatch(t *testing.T, expected, got []attribute.KeyValue, format string, args ...interface{}) { if !assert.ElementsMatchf(t, expected, got, format, args...) { t.Log("expected:", kvStr(expected)) t.Log("got:", kvStr(got)) } } func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request { major, minor := protoToInts(proto) var tlsConn *tls.ConnectionState switch tlsopt { case noTLS: case withTLS: tlsConn = &tls.ConnectionState{} } return &http.Request{ Method: method, URL: u, Proto: proto, ProtoMajor: major, ProtoMinor: minor, Header: header, Host: host, RemoteAddr: remoteAddr, RequestURI: requestURI, TLS: tlsConn, } } func protoToInts(proto string) (int, int) { switch proto { case "HTTP/1.0": return 1, 0 case "HTTP/1.1": return 1, 1 case "HTTP/2.0": return 2, 0 } // invalid proto return 13, 42 } func kvStr(kvs []attribute.KeyValue) string { sb := strings.Builder{} sb.WriteRune('[') for idx, label := range kvs { if idx > 0 { sb.WriteString(", ") } sb.WriteString((string)(label.Key)) sb.WriteString(": ") sb.WriteString(label.Value.Emit()) } sb.WriteRune(']') return sb.String() } func TestHTTPClientAttributesFromHTTPRequest(t *testing.T) { testCases := []struct { name string method string requestURI string proto string remoteAddr string host string url *url.URL header http.Header tls tlsOption contentLength int64 expected []attribute.KeyValue }{ { name: "stripped", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: noTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.String("http.flavor", "1.0"), }, }, { name: "with tls", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), }, }, { name: "with host", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: nil, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), }, }, { name: "with user agent", method: "GET", requestURI: "/user/123", proto: "HTTP/1.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.0"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 1.1", method: "GET", requestURI: "/user/123", proto: "HTTP/1.1", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "1.1"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with http 2", method: "GET", requestURI: "/user/123", proto: "HTTP/2.0", remoteAddr: "", host: "example.com", url: &url.URL{ Path: "/user/123", }, header: http.Header{ "User-Agent": []string{"foodownloader"}, }, tls: withTLS, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "https"), attribute.String("http.flavor", "2"), attribute.String("http.host", "example.com"), attribute.String("http.user_agent", "foodownloader"), }, }, { name: "with content length", method: "GET", url: &url.URL{ Path: "/user/123", }, contentLength: 100, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), attribute.Int64("http.request_content_length", 100), }, }, { name: "with empty method (fallback to GET)", method: "", url: &url.URL{ Path: "/user/123", }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, { name: "authentication information is stripped", method: "", url: &url.URL{ Path: "/user/123", User: url.UserPassword("foo", "bar"), }, expected: []attribute.KeyValue{ attribute.String("http.method", "GET"), attribute.String("http.url", "/user/123"), attribute.String("http.scheme", "http"), }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls) r.ContentLength = tc.contentLength got := HTTPClientAttributesFromHTTPRequest(r) assert.ElementsMatch(t, tc.expected, got) }) } } golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/resource.go000066400000000000000000000761521414226744000233130ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" import "go.opentelemetry.io/otel/attribute" // A cloud environment (e.g. GCP, Azure, AWS) const ( // Name of the cloud provider. // // Type: Enum // Required: No // Stability: stable CloudProviderKey = attribute.Key("cloud.provider") // The cloud account ID the resource is assigned to. // // Type: string // Required: No // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") // The geographical region the resource is running. Refer to your provider's docs // to see the available regions, for example [Alibaba Cloud // regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), // [Azure regions](https://azure.microsoft.com/en-us/global- // infrastructure/geographies/), or [Google Cloud // regions](https://cloud.google.com/about/locations). // // Type: string // Required: No // Stability: stable // Examples: 'us-central1', 'us-east-1' CloudRegionKey = attribute.Key("cloud.region") // Cloud regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the resource // is running. // // Type: string // Required: No // Stability: stable // Examples: 'us-east-1c' // Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud. CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") // The cloud platform in use. // // Type: Enum // Required: No // Stability: stable // Note: The prefix of the service SHOULD match the one specified in // `cloud.provider`. CloudPlatformKey = attribute.Key("cloud.platform") ) var ( // Alibaba Cloud CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud") // Amazon Web Services CloudProviderAWS = CloudProviderKey.String("aws") // Microsoft Azure CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") ) var ( // Alibaba Cloud Elastic Compute Service CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs") // Alibaba Cloud Function Compute CloudPlatformAlibabaCloudFc = CloudPlatformKey.String("alibaba_cloud_fc") // AWS Elastic Compute Cloud CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") // AWS Elastic Container Service CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") // AWS Elastic Kubernetes Service CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") // AWS Lambda CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") // Azure Kubernetes Service CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") // Azure Functions CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") // Azure App Service CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") // Google Cloud Compute Engine (GCE) CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") // Google Cloud Run CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") // Google Cloud Kubernetes Engine (GKE) CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") // Google Cloud Functions (GCF) CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") ) // Resources used by AWS Elastic Container Service (ECS). const ( // The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws. // amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") // The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo // perguide/clusters.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") // The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l // aunch_types.html) for an ECS task. // // Type: Enum // Required: No // Stability: stable AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") // The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates // t/developerguide/task_definitions.html). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us- // west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") // The task definition family this task definition is a member of. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-family' AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") // The revision for this task definition. // // Type: string // Required: No // Stability: stable // Examples: '8', '26' AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") ) var ( // ec2 AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") // fargate AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") ) // Resources used by AWS Elastic Kubernetes Service (EKS). const ( // The ARN of an EKS cluster. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") ) // Resources specific to Amazon Web Services. const ( // The name(s) of the AWS log group(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: '/aws/lambda/my-function', 'opentelemetry-service' // Note: Multiple log groups must be supported for cases like multi-container // applications, where a single application has sidecar containers, and each write // to their own log group. AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") // The Amazon Resource Name(s) (ARN) of the AWS log group(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' // Note: See the [log group ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") // The name(s) of the AWS log stream(s) an application is writing to. // // Type: string[] // Required: No // Stability: stable // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") // The ARN(s) of the AWS log stream(s). // // Type: string[] // Required: No // Stability: stable // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log- // stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' // Note: See the [log stream ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- // access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain // several log streams, so these ARNs necessarily identify both a log group and a // log stream. AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") ) // A container instance. const ( // Container name. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-autoconf' ContainerNameKey = attribute.Key("container.name") // Container ID. Usually a UUID, as for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container- // identification). The UUID might be abbreviated. // // Type: string // Required: No // Stability: stable // Examples: 'a3bf90e006b2' ContainerIDKey = attribute.Key("container.id") // The container runtime managing this container. // // Type: string // Required: No // Stability: stable // Examples: 'docker', 'containerd', 'rkt' ContainerRuntimeKey = attribute.Key("container.runtime") // Name of the image the container was built on. // // Type: string // Required: No // Stability: stable // Examples: 'gcr.io/opentelemetry/operator' ContainerImageNameKey = attribute.Key("container.image.name") // Container image tag. // // Type: string // Required: No // Stability: stable // Examples: '0.1' ContainerImageTagKey = attribute.Key("container.image.tag") ) // The software deployment. const ( // Name of the [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). // // Type: string // Required: No // Stability: stable // Examples: 'staging', 'production' DeploymentEnvironmentKey = attribute.Key("deployment.environment") ) // The device on which the process represented by this resource is running. const ( // A unique identifier representing the device // // Type: string // Required: No // Stability: stable // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' // Note: The device identifier MUST only be defined using the values outlined // below. This value is not an advertising identifier and MUST NOT be used as // such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id // entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden // tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the // Firebase Installation ID or a globally unique UUID which is persisted across // sessions in your application. More information can be found // [here](https://developer.android.com/training/articles/user-data-ids) on best // practices and exact implementation details. Caution should be taken when // storing personal data or anything which can identify a user. GDPR and data // protection laws may apply, ensure you do your own due diligence. DeviceIDKey = attribute.Key("device.id") // The model identifier for the device // // Type: string // Required: No // Stability: stable // Examples: 'iPhone3,4', 'SM-G920F' // Note: It's recommended this value represents a machine readable version of the // model identifier rather than the market or consumer-friendly name of the // device. DeviceModelIdentifierKey = attribute.Key("device.model.identifier") // The marketing name for the device model // // Type: string // Required: No // Stability: stable // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' // Note: It's recommended this value represents a human readable version of the // device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") ) // A serverless instance. const ( // The name of the single function that this runtime instance executes. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: This is the name of the function as configured/deployed on the FaaS // platform and is usually different from the name of the callback function (which // may be stored in the // [`code.namespace`/`code.function`](../../trace/semantic_conventions/span- // general.md#source-code-attributes) span attributes). FaaSNameKey = attribute.Key("faas.name") // The unique ID of the single function that this runtime instance executes. // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' // Note: Depending on the cloud provider, use: // * **AWS Lambda:** The function // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and- // namespaces.html). // Take care not to use the "invoked ARN" directly but replace any // [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // aliases.html) with the resolved function version, as the same runtime instance // may be invokable with multiple // different aliases. // * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full- // resource-names) // * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en- // us/rest/api/resources/resources/get-by-id). // On some providers, it may not be possible to determine the full ID at startup, // which is why this field cannot be made required. For example, on AWS the // account ID // part of the ARN is not available without calling another AWS API // which may be deemed too slow for a short-running lambda function. // As an alternative, consider setting `faas.id` as a span attribute instead. FaaSIDKey = attribute.Key("faas.id") // The immutable version of the function being executed. // // Type: string // Required: No // Stability: stable // Examples: '26', 'pinkfroid-00002' // Note: Depending on the cloud provider and platform, use: // * **AWS Lambda:** The [function // version](https://docs.aws.amazon.com/lambda/latest/dg/configuration- // versions.html) // (an integer represented as a decimal string). // * **Google Cloud Run:** The // [revision](https://cloud.google.com/run/docs/managing/revisions) // (i.e., the function name plus the revision suffix). // * **Google Cloud Functions:** The value of the // [`K_REVISION` environment // variable](https://cloud.google.com/functions/docs/env- // var#runtime_environment_variables_set_automatically). // * **Azure Functions:** Not applicable. Do not set this attribute. FaaSVersionKey = attribute.Key("faas.version") // The execution environment ID as a string, that will be potentially reused for // other invocations to the same function/function version. // // Type: string // Required: No // Stability: stable // Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de' // Note: * **AWS Lambda:** Use the (full) log stream name. FaaSInstanceKey = attribute.Key("faas.instance") // The amount of memory available to the serverless function in MiB. // // Type: int // Required: No // Stability: stable // Examples: 128 // Note: It's recommended to set this attribute since e.g. too little memory can // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this // information. FaaSMaxMemoryKey = attribute.Key("faas.max_memory") ) // A host is defined as a general computing instance. const ( // Unique host ID. For Cloud, this must be the instance_id assigned by the cloud // provider. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostIDKey = attribute.Key("host.id") // Name of the host. On Unix systems, it may contain what the hostname command // returns, or the fully qualified hostname, or another name specified by the // user. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-test' HostNameKey = attribute.Key("host.name") // Type of host. For Cloud, this must be the machine type. // // Type: string // Required: No // Stability: stable // Examples: 'n1-standard-1' HostTypeKey = attribute.Key("host.type") // The CPU architecture the host system is running on. // // Type: Enum // Required: No // Stability: stable HostArchKey = attribute.Key("host.arch") // Name of the VM image or OS install the host was instantiated from. // // Type: string // Required: No // Stability: stable // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' HostImageNameKey = attribute.Key("host.image.name") // VM image ID. For Cloud, this value is from the provider. // // Type: string // Required: No // Stability: stable // Examples: 'ami-07b06b442921831e5' HostImageIDKey = attribute.Key("host.image.id") // The version string of the VM image as defined in [Version // Attributes](README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '0.1' HostImageVersionKey = attribute.Key("host.image.version") ) var ( // AMD64 HostArchAMD64 = HostArchKey.String("amd64") // ARM32 HostArchARM32 = HostArchKey.String("arm32") // ARM64 HostArchARM64 = HostArchKey.String("arm64") // Itanium HostArchIA64 = HostArchKey.String("ia64") // 32-bit PowerPC HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) // A Kubernetes Cluster. const ( // The name of the cluster. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-cluster' K8SClusterNameKey = attribute.Key("k8s.cluster.name") ) // A Kubernetes Node object. const ( // The name of the Node. // // Type: string // Required: No // Stability: stable // Examples: 'node-1' K8SNodeNameKey = attribute.Key("k8s.node.name") // The UID of the Node. // // Type: string // Required: No // Stability: stable // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' K8SNodeUIDKey = attribute.Key("k8s.node.uid") ) // A Kubernetes Namespace. const ( // The name of the namespace that the pod is running in. // // Type: string // Required: No // Stability: stable // Examples: 'default' K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") ) // A Kubernetes Pod object. const ( // The UID of the Pod. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SPodUIDKey = attribute.Key("k8s.pod.uid") // The name of the Pod. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry-pod-autoconf' K8SPodNameKey = attribute.Key("k8s.pod.name") ) // A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( // The name of the Container in a Pod template. // // Type: string // Required: No // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") ) // A Kubernetes ReplicaSet object. const ( // The UID of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") // The name of the ReplicaSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") ) // A Kubernetes Deployment object. const ( // The UID of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") // The name of the Deployment. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") ) // A Kubernetes StatefulSet object. const ( // The UID of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") // The name of the StatefulSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") ) // A Kubernetes DaemonSet object. const ( // The UID of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") // The name of the DaemonSet. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") ) // A Kubernetes Job object. const ( // The UID of the Job. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SJobUIDKey = attribute.Key("k8s.job.uid") // The name of the Job. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SJobNameKey = attribute.Key("k8s.job.name") ) // A Kubernetes CronJob object. const ( // The UID of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") // The name of the CronJob. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") ) // The operating system (OS) on which the process represented by this resource is running. const ( // The operating system type. // // Type: Enum // Required: Always // Stability: stable OSTypeKey = attribute.Key("os.type") // Human readable (not intended to be parsed) OS version information, like e.g. // reported by `ver` or `lsb_release -a` commands. // // Type: string // Required: No // Stability: stable // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS' OSDescriptionKey = attribute.Key("os.description") // Human readable operating system name. // // Type: string // Required: No // Stability: stable // Examples: 'iOS', 'Android', 'Ubuntu' OSNameKey = attribute.Key("os.name") // The version string of the operating system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // Required: No // Stability: stable // Examples: '14.2.1', '18.04.1' OSVersionKey = attribute.Key("os.version") ) var ( // Microsoft Windows OSTypeWindows = OSTypeKey.String("windows") // Linux OSTypeLinux = OSTypeKey.String("linux") // Apple Darwin OSTypeDarwin = OSTypeKey.String("darwin") // FreeBSD OSTypeFreeBSD = OSTypeKey.String("freebsd") // NetBSD OSTypeNetBSD = OSTypeKey.String("netbsd") // OpenBSD OSTypeOpenBSD = OSTypeKey.String("openbsd") // DragonFly BSD OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") // HP-UX (Hewlett Packard Unix) OSTypeHPUX = OSTypeKey.String("hpux") // AIX (Advanced Interactive eXecutive) OSTypeAIX = OSTypeKey.String("aix") // Oracle Solaris OSTypeSolaris = OSTypeKey.String("solaris") // IBM z/OS OSTypeZOS = OSTypeKey.String("z_os") ) // An operating system process. const ( // Process identifier (PID). // // Type: int // Required: No // Stability: stable // Examples: 1234 ProcessPIDKey = attribute.Key("process.pid") // The name of the process executable. On Linux based systems, can be set to the // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: 'otelcol' ProcessExecutableNameKey = attribute.Key("process.executable.name") // The full path to the process executable. On Linux based systems, can be set to // the target of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. // // Type: string // Required: See below // Stability: stable // Examples: '/usr/bin/cmd/otelcol' ProcessExecutablePathKey = attribute.Key("process.executable.path") // The command used to launch the process (i.e. the command name). On Linux based // systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, // can be set to the first parameter extracted from `GetCommandLineW`. // // Type: string // Required: See below // Stability: stable // Examples: 'cmd/otelcol' ProcessCommandKey = attribute.Key("process.command") // The full command used to launch the process as a single string representing the // full command. On Windows, can be set to the result of `GetCommandLineW`. Do not // set this if you have to assemble it just for monitoring; use // `process.command_args` instead. // // Type: string // Required: See below // Stability: stable // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' ProcessCommandLineKey = attribute.Key("process.command_line") // All the command arguments (including the command/executable itself) as received // by the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited strings // extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be // the full argv vector passed to `main`. // // Type: string[] // Required: See below // Stability: stable // Examples: 'cmd/otecol', '--config=config.yaml' ProcessCommandArgsKey = attribute.Key("process.command_args") // The username of the user that owns the process. // // Type: string // Required: No // Stability: stable // Examples: 'root' ProcessOwnerKey = attribute.Key("process.owner") ) // The single (language) runtime instance which is monitored. const ( // The name of the runtime of this process. For compiled native binaries, this // SHOULD be the name of the compiler. // // Type: string // Required: No // Stability: stable // Examples: 'OpenJDK Runtime Environment' ProcessRuntimeNameKey = attribute.Key("process.runtime.name") // The version of the runtime of this process, as returned by the runtime without // modification. // // Type: string // Required: No // Stability: stable // Examples: '14.0.2' ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") // An additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. // // Type: string // Required: No // Stability: stable // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") ) // A service instance. const ( // Logical name of the service. // // Type: string // Required: Always // Stability: stable // Examples: 'shoppingcart' // Note: MUST be the same for all instances of horizontally scaled services. If // the value was not specified, SDKs MUST fallback to `unknown_service:` // concatenated with [`process.executable.name`](process.md#process), e.g. // `unknown_service:bash`. If `process.executable.name` is not available, the // value MUST be set to `unknown_service`. ServiceNameKey = attribute.Key("service.name") // A namespace for `service.name`. // // Type: string // Required: No // Stability: stable // Examples: 'Shop' // Note: A string value having a meaning that helps to distinguish a group of // services, for example the team name that owns a group of services. // `service.name` is expected to be unique within the same namespace. If // `service.namespace` is not specified in the Resource then `service.name` is // expected to be unique for all services that have no explicit namespace defined // (so the empty/unspecified namespace is simply one more valid namespace). Zero- // length namespace string is assumed equal to unspecified namespace. ServiceNamespaceKey = attribute.Key("service.namespace") // The string ID of the service instance. // // Type: string // Required: No // Stability: stable // Examples: '627cc493-f310-47de-96bd-71410b7dec09' // Note: MUST be unique for each instance of the same // `service.namespace,service.name` pair (in other words // `service.namespace,service.name,service.instance.id` triplet MUST be globally // unique). The ID helps to distinguish instances of the same service that exist // at the same time (e.g. instances of a horizontally scaled service). It is // preferable for the ID to be persistent and stay the same for the lifetime of // the service instance, however it is acceptable that the ID is ephemeral and // changes during important lifetime events for the service (e.g. service // restarts). If the service has no inherent unique ID that can be used as the // value of this attribute it is recommended to generate a random Version 1 or // Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use // Version 5, see RFC 4122 for more recommendations). ServiceInstanceIDKey = attribute.Key("service.instance.id") // The version string of the service API or implementation. // // Type: string // Required: No // Stability: stable // Examples: '2.0.0' ServiceVersionKey = attribute.Key("service.version") ) // The telemetry SDK used to capture data recorded by the instrumentation libraries. const ( // The name of the telemetry SDK as defined above. // // Type: string // Required: No // Stability: stable // Examples: 'opentelemetry' TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") // The language of the telemetry SDK. // // Type: Enum // Required: No // Stability: stable TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") // The version string of the telemetry SDK. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") // The version string of the auto instrumentation agent, if used. // // Type: string // Required: No // Stability: stable // Examples: '1.2.3' TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") ) var ( // cpp TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") // dotnet TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") // erlang TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") // go TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") // java TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") // nodejs TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") // php TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") // python TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") // ruby TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") ) // Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. const ( // The name of the web engine. // // Type: string // Required: Always // Stability: stable // Examples: 'WildFly' WebEngineNameKey = attribute.Key("webengine.name") // The version of the web engine. // // Type: string // Required: No // Stability: stable // Examples: '21.0.0' WebEngineVersionKey = attribute.Key("webengine.version") // Additional description of the web engine (e.g. detailed version and edition // information). // // Type: string // Required: No // Stability: stable // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final' WebEngineDescriptionKey = attribute.Key("webengine.description") ) golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/schema.go000066400000000000000000000017131414226744000227130ustar00rootroot00000000000000// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ const SchemaURL = "https://opentelemetry.io/schemas/v1.7.0" golang-opentelemetry-otel-1.1.0/semconv/v1.7.0/trace.go000066400000000000000000001546311414226744000225610ustar00rootroot00000000000000// 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. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" import "go.opentelemetry.io/otel/attribute" // Span attributes used by AWS Lambda (in addition to general `faas` attributes). const ( // The full invoked ARN as provided on the `Context` passed to the function // (`Lambda-Runtime-Invoked-Function-ARN` header on the `/runtime/invocation/next` // applicable). // // Type: string // Required: No // Stability: stable // Examples: 'arn:aws:lambda:us-east-1:123456:function:myfunction:myalias' // Note: This may be different from `faas.id` if an alias is involved. AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") ) // This document defines the attributes used to perform database client calls. const ( // An identifier for the database management system (DBMS) product being used. See // below for a list of well-known identifiers. // // Type: Enum // Required: Always // Stability: stable DBSystemKey = attribute.Key("db.system") // The connection string used to connect to the database. It is recommended to // remove embedded credentials. // // Type: string // Required: No // Stability: stable // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' DBConnectionStringKey = attribute.Key("db.connection_string") // Username for accessing the database. // // Type: string // Required: No // Stability: stable // Examples: 'readonly_user', 'reporting_user' DBUserKey = attribute.Key("db.user") // The fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver // used to connect. // // Type: string // Required: No // Stability: stable // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") // If no [tech-specific attribute](#call-level-attributes-for-specific- // technologies) is defined, this attribute is used to report the name of the // database being accessed. For commands that switch the database, this should be // set to the target database (even if the command fails). // // Type: string // Required: Required, if applicable and no more-specific attribute is defined. // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called "schema // name". DBNameKey = attribute.Key("db.name") // The database statement being executed. // // Type: string // Required: Required if applicable and not explicitly disabled via // instrumentation configuration. // Stability: stable // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' // Note: The value may be sanitized to exclude sensitive information. DBStatementKey = attribute.Key("db.statement") // The name of the operation being executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. // // Type: string // Required: Required, if `db.statement` is not applicable. // Stability: stable // Examples: 'findAndModify', 'HMSET', 'SELECT' // Note: When setting this to an SQL keyword, it is not recommended to attempt any // client-side parsing of `db.statement` just to get this property, but it should // be set if the operation name is provided by the library being instrumented. If // the SQL statement has an ambiguous operation, or performs more than one // operation, this value may be omitted. DBOperationKey = attribute.Key("db.operation") ) var ( // Some other SQL database. Fallback only. See notes DBSystemOtherSQL = DBSystemKey.String("other_sql") // Microsoft SQL Server DBSystemMSSQL = DBSystemKey.String("mssql") // MySQL DBSystemMySQL = DBSystemKey.String("mysql") // Oracle Database DBSystemOracle = DBSystemKey.String("oracle") // IBM DB2 DBSystemDB2 = DBSystemKey.String("db2") // PostgreSQL DBSystemPostgreSQL = DBSystemKey.String("postgresql") // Amazon Redshift DBSystemRedshift = DBSystemKey.String("redshift") // Apache Hive DBSystemHive = DBSystemKey.String("hive") // Cloudscape DBSystemCloudscape = DBSystemKey.String("cloudscape") // HyperSQL DataBase DBSystemHSQLDB = DBSystemKey.String("hsqldb") // Progress Database DBSystemProgress = DBSystemKey.String("progress") // SAP MaxDB DBSystemMaxDB = DBSystemKey.String("maxdb") // SAP HANA DBSystemHanaDB = DBSystemKey.String("hanadb") // Ingres DBSystemIngres = DBSystemKey.String("ingres") // FirstSQL DBSystemFirstSQL = DBSystemKey.String("firstsql") // EnterpriseDB DBSystemEDB = DBSystemKey.String("edb") // InterSystems Caché DBSystemCache = DBSystemKey.String("cache") // Adabas (Adaptable Database System) DBSystemAdabas = DBSystemKey.String("adabas") // Firebird DBSystemFirebird = DBSystemKey.String("firebird") // Apache Derby DBSystemDerby = DBSystemKey.String("derby") // FileMaker DBSystemFilemaker = DBSystemKey.String("filemaker") // Informix DBSystemInformix = DBSystemKey.String("informix") // InstantDB DBSystemInstantDB = DBSystemKey.String("instantdb") // InterBase DBSystemInterbase = DBSystemKey.String("interbase") // MariaDB DBSystemMariaDB = DBSystemKey.String("mariadb") // Netezza DBSystemNetezza = DBSystemKey.String("netezza") // Pervasive PSQL DBSystemPervasive = DBSystemKey.String("pervasive") // PointBase DBSystemPointbase = DBSystemKey.String("pointbase") // SQLite DBSystemSqlite = DBSystemKey.String("sqlite") // Sybase DBSystemSybase = DBSystemKey.String("sybase") // Teradata DBSystemTeradata = DBSystemKey.String("teradata") // Vertica DBSystemVertica = DBSystemKey.String("vertica") // H2 DBSystemH2 = DBSystemKey.String("h2") // ColdFusion IMQ DBSystemColdfusion = DBSystemKey.String("coldfusion") // Apache Cassandra DBSystemCassandra = DBSystemKey.String("cassandra") // Apache HBase DBSystemHBase = DBSystemKey.String("hbase") // MongoDB DBSystemMongoDB = DBSystemKey.String("mongodb") // Redis DBSystemRedis = DBSystemKey.String("redis") // Couchbase DBSystemCouchbase = DBSystemKey.String("couchbase") // CouchDB DBSystemCouchDB = DBSystemKey.String("couchdb") // Microsoft Azure Cosmos DB DBSystemCosmosDB = DBSystemKey.String("cosmosdb") // Amazon DynamoDB DBSystemDynamoDB = DBSystemKey.String("dynamodb") // Neo4j DBSystemNeo4j = DBSystemKey.String("neo4j") // Apache Geode DBSystemGeode = DBSystemKey.String("geode") // Elasticsearch DBSystemElasticsearch = DBSystemKey.String("elasticsearch") // Memcached DBSystemMemcached = DBSystemKey.String("memcached") // CockroachDB DBSystemCockroachdb = DBSystemKey.String("cockroachdb") ) // Connection-level attributes for Microsoft SQL Server const ( // The Microsoft SQL Server [instance name](https://docs.microsoft.com/en- // us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named instance. // // Type: string // Required: No // Stability: stable // Examples: 'MSSQLSERVER' // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer // required (but still recommended if non-standard). DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") ) // Call-level attributes for Cassandra const ( // The name of the keyspace being accessed. To be used instead of the generic // `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'mykeyspace' DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") // The fetch size used for paging, i.e. how many rows will be returned at once. // // Type: int // Required: No // Stability: stable // Examples: 5000 DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") // The consistency level of the query. Based on consistency values from // [CQL](https://docs.datastax.com/en/cassandra- // oss/3.0/cassandra/dml/dmlConfigConsistency.html). // // Type: Enum // Required: No // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'mytable' // Note: This mirrors the db.sql.table attribute but references cassandra rather // than sql. It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBCassandraTableKey = attribute.Key("db.cassandra.table") // Whether or not the query is idempotent. // // Type: boolean // Required: No // Stability: stable DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") // The number of times a query was speculatively executed. Not set or `0` if the // query was not executed speculatively. // // Type: int // Required: No // Stability: stable // Examples: 0, 2 DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") // The ID of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") // The data center of the coordinating node for a query. // // Type: string // Required: No // Stability: stable // Examples: 'us-west-2' DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") ) var ( // all DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") // each_quorum DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") // quorum DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") // local_quorum DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") // one DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") // two DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") // three DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") // local_one DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") // any DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") // serial DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") // local_serial DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) // Call-level attributes for Apache HBase const ( // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being // accessed. To be used instead of the generic `db.name` attribute. // // Type: string // Required: Always // Stability: stable // Examples: 'default' DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") ) // Call-level attributes for Redis const ( // The index of the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To be used // instead of the generic `db.name` attribute. // // Type: int // Required: Required, if other than the default database (`0`). // Stability: stable // Examples: 0, 1, 15 DBRedisDBIndexKey = attribute.Key("db.redis.database_index") ) // Call-level attributes for MongoDB const ( // The collection being accessed within the database stated in `db.name`. // // Type: string // Required: Always // Stability: stable // Examples: 'customers', 'products' DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) // Call-level attrbiutes for SQL databases const ( // The name of the primary table that the operation is acting upon, including the // schema name (if applicable). // // Type: string // Required: Recommended if available. // Stability: stable // Examples: 'public.users', 'customers' // Note: It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting upon an // anonymous table, or more than one table, this value MUST NOT be set. DBSQLTableKey = attribute.Key("db.sql.table") ) // This document defines the attributes used to report a single exception associated with a span. const ( // The type of the exception (its fully-qualified class name, if applicable). The // dynamic type of the exception should be preferred over the static type in // languages that support it. // // Type: string // Required: No // Stability: stable // Examples: 'java.net.ConnectException', 'OSError' ExceptionTypeKey = attribute.Key("exception.type") // The exception message. // // Type: string // Required: No // Stability: stable // Examples: 'Division by zero', "Can't convert 'int' object to str implicitly" ExceptionMessageKey = attribute.Key("exception.message") // A stacktrace as a string in the natural representation for the language // runtime. The representation is to be determined and documented by each language // SIG. // // Type: string // Required: No // Stability: stable // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test // exception\\n at ' // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' ExceptionStacktraceKey = attribute.Key("exception.stacktrace") // SHOULD be set to true if the exception event is recorded at a point where it is // known that the exception is escaping the scope of the span. // // Type: boolean // Required: No // Stability: stable // Note: An exception is considered to have escaped (or left) the scope of a span, // if that span is ended while the exception is still logically "in flight". // This may be actually "in flight" in some languages (e.g. if the exception // is passed to a Context manager's `__exit__` method in Python) but will // usually be caught at the point of recording the exception in most languages. // It is usually not possible to determine at the point where an exception is // thrown // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending the span, // as done in the [example above](#exception-end-example). // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, // since the event might have been recorded at a time where it was not // clear whether the exception will escape. ExceptionEscapedKey = attribute.Key("exception.escaped") ) // This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. const ( // Type of the trigger on which the function is executed. // // Type: Enum // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing // invocations, if it is known to the client. This is, for example, not the case, // when the transport layer is abstracted in a FaaS client framework without // access to its configuration. // Stability: stable FaaSTriggerKey = attribute.Key("faas.trigger") // The execution ID of the current function execution. // // Type: string // Required: No // Stability: stable // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' FaaSExecutionKey = attribute.Key("faas.execution") ) var ( // A response to some data source operation such as a database or filesystem read/write FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") // To provide an answer to an inbound HTTP request FaaSTriggerHTTP = FaaSTriggerKey.String("http") // A function is set to be executed when messages are sent to a messaging system FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") // A function is scheduled to be executed regularly FaaSTriggerTimer = FaaSTriggerKey.String("timer") // If none of the others apply FaaSTriggerOther = FaaSTriggerKey.String("other") ) // Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. const ( // The name of the source on which the triggering operation was performed. For // example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos // DB to the database name. // // Type: string // Required: Always // Stability: stable // Examples: 'myBucketName', 'myDBName' FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") // Describes the type of the operation that was performed on the data. // // Type: Enum // Required: Always // Stability: stable FaaSDocumentOperationKey = attribute.Key("faas.document.operation") // A string containing the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSDocumentTimeKey = attribute.Key("faas.document.time") // The document name/table subjected to the operation. For example, in Cloud // Storage or S3 is the name of the file, and in Cosmos DB the table name. // // Type: string // Required: No // Stability: stable // Examples: 'myFile.txt', 'myTableName' FaaSDocumentNameKey = attribute.Key("faas.document.name") ) var ( // When a new object is created FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") // When an object is modified FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") // When an object is deleted FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") ) // Semantic Convention for FaaS scheduled to be executed regularly. const ( // A string containing the function invocation time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed // in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // Required: Always // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSTimeKey = attribute.Key("faas.time") // A string containing the schedule period as [Cron Expression](https://docs.oracl // e.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). // // Type: string // Required: No // Stability: stable // Examples: '0/5 * * * ? *' FaaSCronKey = attribute.Key("faas.cron") ) // Contains additional attributes for incoming FaaS spans. const ( // A boolean that is true if the serverless function is executed for the first // time (aka cold-start). // // Type: boolean // Required: No // Stability: stable FaaSColdstartKey = attribute.Key("faas.coldstart") ) // Contains additional attributes for outgoing FaaS spans. const ( // The name of the invoked function. // // Type: string // Required: Always // Stability: stable // Examples: 'my-function' // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked // function. FaaSInvokedNameKey = attribute.Key("faas.invoked_name") // The cloud provider of the invoked function. // // Type: Enum // Required: Always // Stability: stable // Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked // function. FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") // The cloud region of the invoked function. // // Type: string // Required: For some cloud providers, like AWS or GCP, the region in which a // function is hosted is essential to uniquely identify the function and also part // of its endpoint. Since it's part of the endpoint being called, the region is // always known to clients. In these cases, `faas.invoked_region` MUST be set // accordingly. If the region is unknown to the client or not required for // identifying the invoked function, setting `faas.invoked_region` is optional. // Stability: stable // Examples: 'eu-central-1' // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked // function. FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") ) var ( // Alibaba Cloud FaaSInvokedProviderAlibabaCloud = FaaSInvokedProviderKey.String("alibaba_cloud") // Amazon Web Services FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") // Microsoft Azure FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") ) // These attributes may be used for any network related operation. const ( // Transport protocol used. See note below. // // Type: Enum // Required: No // Stability: stable NetTransportKey = attribute.Key("net.transport") // Remote address of the peer (dotted decimal for IPv4 or // [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6) // // Type: string // Required: No // Stability: stable // Examples: '127.0.0.1' NetPeerIPKey = attribute.Key("net.peer.ip") // Remote port number. // // Type: int // Required: No // Stability: stable // Examples: 80, 8080, 443 NetPeerPortKey = attribute.Key("net.peer.port") // Remote hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'example.com' NetPeerNameKey = attribute.Key("net.peer.name") // Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. // // Type: string // Required: No // Stability: stable // Examples: '192.168.0.1' NetHostIPKey = attribute.Key("net.host.ip") // Like `net.peer.port` but for the host port. // // Type: int // Required: No // Stability: stable // Examples: 35555 NetHostPortKey = attribute.Key("net.host.port") // Local hostname or similar, see note below. // // Type: string // Required: No // Stability: stable // Examples: 'localhost' NetHostNameKey = attribute.Key("net.host.name") // The internet connection type currently being used by the host. // // Type: Enum // Required: No // Stability: stable // Examples: 'wifi' NetHostConnectionTypeKey = attribute.Key("net.host.connection.type") // This describes more details regarding the connection.type. It may be the type // of cell technology connection, but it could be used for describing details // about a wifi connection. // // Type: Enum // Required: No // Stability: stable // Examples: 'LTE' NetHostConnectionSubtypeKey = attribute.Key("net.host.connection.subtype") // The name of the mobile carrier. // // Type: string // Required: No // Stability: stable // Examples: 'sprint' NetHostCarrierNameKey = attribute.Key("net.host.carrier.name") // The mobile carrier country code. // // Type: string // Required: No // Stability: stable // Examples: '310' NetHostCarrierMccKey = attribute.Key("net.host.carrier.mcc") // The mobile carrier network code. // // Type: string // Required: No // Stability: stable // Examples: '001' NetHostCarrierMncKey = attribute.Key("net.host.carrier.mnc") // The ISO 3166-1 alpha-2 2-character country code associated with the mobile // carrier network. // // Type: string // Required: No // Stability: stable // Examples: 'DE' NetHostCarrierIccKey = attribute.Key("net.host.carrier.icc") ) var ( // ip_tcp NetTransportTCP = NetTransportKey.String("ip_tcp") // ip_udp NetTransportUDP = NetTransportKey.String("ip_udp") // Another IP-based protocol NetTransportIP = NetTransportKey.String("ip") // Unix Domain socket. See below NetTransportUnix = NetTransportKey.String("unix") // Named or anonymous pipe. See note below NetTransportPipe = NetTransportKey.String("pipe") // In-process communication NetTransportInProc = NetTransportKey.String("inproc") // Something else (non IP-based) NetTransportOther = NetTransportKey.String("other") ) var ( // wifi NetHostConnectionTypeWifi = NetHostConnectionTypeKey.String("wifi") // wired NetHostConnectionTypeWired = NetHostConnectionTypeKey.String("wired") // cell NetHostConnectionTypeCell = NetHostConnectionTypeKey.String("cell") // unavailable NetHostConnectionTypeUnavailable = NetHostConnectionTypeKey.String("unavailable") // unknown NetHostConnectionTypeUnknown = NetHostConnectionTypeKey.String("unknown") ) var ( // GPRS NetHostConnectionSubtypeGprs = NetHostConnectionSubtypeKey.String("gprs") // EDGE NetHostConnectionSubtypeEdge = NetHostConnectionSubtypeKey.String("edge") // UMTS NetHostConnectionSubtypeUmts = NetHostConnectionSubtypeKey.String("umts") // CDMA NetHostConnectionSubtypeCdma = NetHostConnectionSubtypeKey.String("cdma") // EVDO Rel. 0 NetHostConnectionSubtypeEvdo0 = NetHostConnectionSubtypeKey.String("evdo_0") // EVDO Rev. A NetHostConnectionSubtypeEvdoA = NetHostConnectionSubtypeKey.String("evdo_a") // CDMA2000 1XRTT NetHostConnectionSubtypeCdma20001xrtt = NetHostConnectionSubtypeKey.String("cdma2000_1xrtt") // HSDPA NetHostConnectionSubtypeHsdpa = NetHostConnectionSubtypeKey.String("hsdpa") // HSUPA NetHostConnectionSubtypeHsupa = NetHostConnectionSubtypeKey.String("hsupa") // HSPA NetHostConnectionSubtypeHspa = NetHostConnectionSubtypeKey.String("hspa") // IDEN NetHostConnectionSubtypeIden = NetHostConnectionSubtypeKey.String("iden") // EVDO Rev. B NetHostConnectionSubtypeEvdoB = NetHostConnectionSubtypeKey.String("evdo_b") // LTE NetHostConnectionSubtypeLte = NetHostConnectionSubtypeKey.String("lte") // EHRPD NetHostConnectionSubtypeEhrpd = NetHostConnectionSubtypeKey.String("ehrpd") // HSPAP NetHostConnectionSubtypeHspap = NetHostConnectionSubtypeKey.String("hspap") // GSM NetHostConnectionSubtypeGsm = NetHostConnectionSubtypeKey.String("gsm") // TD-SCDMA NetHostConnectionSubtypeTdScdma = NetHostConnectionSubtypeKey.String("td_scdma") // IWLAN NetHostConnectionSubtypeIwlan = NetHostConnectionSubtypeKey.String("iwlan") // 5G NR (New Radio) NetHostConnectionSubtypeNr = NetHostConnectionSubtypeKey.String("nr") // 5G NRNSA (New Radio Non-Standalone) NetHostConnectionSubtypeNrnsa = NetHostConnectionSubtypeKey.String("nrnsa") // LTE CA NetHostConnectionSubtypeLteCa = NetHostConnectionSubtypeKey.String("lte_ca") ) // Operations that access some remote service. const ( // The [`service.name`](../../resource/semantic_conventions/README.md#service) of // the remote service. SHOULD be equal to the actual `service.name` resource // attribute of the remote service if any. // // Type: string // Required: No // Stability: stable // Examples: 'AuthTokenCache' PeerServiceKey = attribute.Key("peer.service") ) // These attributes may be used for any operation with an authenticated and/or authorized enduser. const ( // Username or client_id extracted from the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the // inbound request from outside the system. // // Type: string // Required: No // Stability: stable // Examples: 'username' EnduserIDKey = attribute.Key("enduser.id") // Actual/assumed role the client is making the request under extracted from token // or application security context. // // Type: string // Required: No // Stability: stable // Examples: 'admin' EnduserRoleKey = attribute.Key("enduser.role") // Scopes or granted authorities the client currently possesses extracted from // token or application security context. The value would come from the scope // associated with an [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value // in a [SAML 2.0 Assertion](http://docs.oasis- // open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). // // Type: string // Required: No // Stability: stable // Examples: 'read:message, write:files' EnduserScopeKey = attribute.Key("enduser.scope") ) // These attributes may be used for any operation to store information about a thread that started a span. const ( // Current "managed" thread ID (as opposed to OS thread ID). // // Type: int // Required: No // Stability: stable // Examples: 42 ThreadIDKey = attribute.Key("thread.id") // Current thread name. // // Type: string // Required: No // Stability: stable // Examples: 'main' ThreadNameKey = attribute.Key("thread.name") ) // These attributes allow to report this unit of code and therefore to provide more context about the span. const ( // The method or function name, or equivalent (usually rightmost part of the code // unit's name). // // Type: string // Required: No // Stability: stable // Examples: 'serveRequest' CodeFunctionKey = attribute.Key("code.function") // The "namespace" within which `code.function` is defined. Usually the qualified // class or module name, such that `code.namespace` + some separator + // `code.function` form a unique identifier for the code unit. // // Type: string // Required: No // Stability: stable // Examples: 'com.example.MyHTTPService' CodeNamespaceKey = attribute.Key("code.namespace") // The source code file name that identifies the code unit as uniquely as possible // (preferably an absolute file path). // // Type: string // Required: No // Stability: stable // Examples: '/usr/local/MyApplication/content_root/app/index.php' CodeFilepathKey = attribute.Key("code.filepath") // The line number in `code.filepath` best representing the operation. It SHOULD // point within the code unit named in `code.function`. // // Type: int // Required: No // Stability: stable // Examples: 42 CodeLineNumberKey = attribute.Key("code.lineno") ) // This document defines semantic conventions for HTTP client and server Spans. const ( // HTTP request method. // // Type: string // Required: Always // Stability: stable // Examples: 'GET', 'POST', 'HEAD' HTTPMethodKey = attribute.Key("http.method") // Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. // Usually the fragment is not transmitted over HTTP, but if it is known, it // should be included nevertheless. // // Type: string // Required: No // Stability: stable // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' // Note: `http.url` MUST NOT contain credentials passed via URL in form of // `https://username:password@www.example.com/`. In such case the attribute's // value should be `https://www.example.com/`. HTTPURLKey = attribute.Key("http.url") // The full request target as passed in a HTTP request line or equivalent. // // Type: string // Required: No // Stability: stable // Examples: '/path/12314/?q=ddds#123' HTTPTargetKey = attribute.Key("http.target") // The value of the [HTTP host // header](https://tools.ietf.org/html/rfc7230#section-5.4). An empty Host header // should also be reported, see note. // // Type: string // Required: No // Stability: stable // Examples: 'www.example.org' // Note: When the header is present but empty the attribute SHOULD be set to the // empty string. Note that this is a valid situation that is expected in certain // cases, according the aforementioned [section of RFC // 7230](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is not // set the attribute MUST NOT be set. HTTPHostKey = attribute.Key("http.host") // The URI scheme identifying the used protocol. // // Type: string // Required: No // Stability: stable // Examples: 'http', 'https' HTTPSchemeKey = attribute.Key("http.scheme") // [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). // // Type: int // Required: If and only if one was received/sent. // Stability: stable // Examples: 200 HTTPStatusCodeKey = attribute.Key("http.status_code") // Kind of HTTP protocol used. // // Type: Enum // Required: No // Stability: stable // Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` // except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. HTTPFlavorKey = attribute.Key("http.flavor") // Value of the [HTTP User- // Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the // client. // // Type: string // Required: No // Stability: stable // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' HTTPUserAgentKey = attribute.Key("http.user_agent") // The size of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") // The size of the uncompressed request payload body after transport decoding. Not // set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPRequestContentLengthUncompressedKey = attribute.Key("http.request_content_length_uncompressed") // The size of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For // requests using transport encoding, this should be the compressed size. // // Type: int // Required: No // Stability: stable // Examples: 3495 HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") // The size of the uncompressed response payload body after transport decoding. // Not set if transport encoding not used. // // Type: int // Required: No // Stability: stable // Examples: 5493 HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") ) var ( // HTTP 1.0 HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") // HTTP 1.1 HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") // HTTP 2 HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") // SPDY protocol HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") // QUIC protocol HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") ) // Semantic Convention for HTTP Server const ( // The primary server name of the matched virtual host. This should be obtained // via configuration. If no such configuration can be obtained, this attribute // MUST NOT be set ( `net.host.name` should be used instead). // // Type: string // Required: No // Stability: stable // Examples: 'example.com' // Note: `http.url` is usually not readily available on the server side but would // have to be assembled in a cumbersome and sometimes lossy process from other // information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus // preferred to supply the raw data that is available. HTTPServerNameKey = attribute.Key("http.server_name") // The matched route (path template). // // Type: string // Required: No // Stability: stable // Examples: '/users/:userID?' HTTPRouteKey = attribute.Key("http.route") // The IP address of the original client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en- // US/docs/Web/HTTP/Headers/X-Forwarded-For)). // // Type: string // Required: No // Stability: stable // Examples: '83.164.160.102' // Note: This is not necessarily the same as `net.peer.ip`, which would // identify the network-level peer, which may be a proxy. // This attribute should be set when a source of information different // from the one used for `net.peer.ip`, is available even if that other // source just confirms the same value as `net.peer.ip`. // Rationale: For `net.peer.ip`, one typically does not know if it // comes from a proxy, reverse proxy, or the actual client. Setting // `http.client_ip` when it's the same as `net.peer.ip` means that // one is at least somewhat confident that the address is not that of // the closest proxy. HTTPClientIPKey = attribute.Key("http.client_ip") ) // Attributes that exist for multiple DynamoDB request types. const ( // The keys in the `RequestItems` object field. // // Type: string[] // Required: No // Stability: stable // Examples: 'Users', 'Cats' AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") // The JSON-serialized value of each item in the `ConsumedCapacity` response // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : { // "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": // "string", "WriteCapacityUnits": number }' AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") // The JSON-serialized value of the `ItemCollectionMetrics` response field. // // Type: string // Required: No // Stability: stable // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }' AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") // The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") // The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. // // Type: double // Required: No // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") // The value of the `ConsistentRead` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") // The value of the `ProjectionExpression` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Title', 'Title, Price, Color', 'Title, Description, RelatedItems, // ProductReviews' AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") // The value of the `Limit` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") // The value of the `AttributesToGet` request parameter. // // Type: string[] // Required: No // Stability: stable // Examples: 'lives', 'id' AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") // The value of the `IndexName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'name_to_group' AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") // The value of the `Select` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'ALL_ATTRIBUTES', 'COUNT' AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") ) // DynamoDB.CreateTable const ( // The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request // field // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" }, "ProvisionedThroughput": { "ReadCapacityUnits": // number, "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") // The JSON-serialized value of each item of the `LocalSecondaryIndexes` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "IndexARN": "string", "IndexName": "string", "IndexSizeBytes": // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], // "ProjectionType": "string" } }' AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") ) // DynamoDB.ListTables const ( // The value of the `ExclusiveStartTableName` request parameter. // // Type: string // Required: No // Stability: stable // Examples: 'Users', 'CatsTable' AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") // The the number of items in the `TableNames` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 20 AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") ) // DynamoDB.Query const ( // The value of the `ScanIndexForward` request parameter. // // Type: boolean // Required: No // Stability: stable AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") ) // DynamoDB.Scan const ( // The value of the `Segment` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") // The value of the `TotalSegments` request parameter. // // Type: int // Required: No // Stability: stable // Examples: 100 AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") // The value of the `Count` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 10 AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") // The value of the `ScannedCount` response parameter. // // Type: int // Required: No // Stability: stable // Examples: 50 AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") ) // DynamoDB.UpdateTable const ( // The JSON-serialized value of each item in the `AttributeDefinitions` request // field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") // The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` // request field. // // Type: string[] // Required: No // Stability: stable // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": // number } }' AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") ) // This document defines the attributes used in messaging systems. const ( // A string identifying the messaging system. // // Type: string // Required: Always // Stability: stable // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // The message destination name. This might be equal to the span name but is // required nevertheless. // // Type: string // Required: Always // Stability: stable // Examples: 'MyQueue', 'MyTopic' MessagingDestinationKey = attribute.Key("messaging.destination") // The kind of message destination // // Type: Enum // Required: Required only if the message destination is either a `queue` or // `topic`. // Stability: stable MessagingDestinationKindKey = attribute.Key("messaging.destination_kind") // A boolean that is true if the message destination is temporary. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingTempDestinationKey = attribute.Key("messaging.temp_destination") // The name of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: 'AMQP', 'MQTT' MessagingProtocolKey = attribute.Key("messaging.protocol") // The version of the transport protocol. // // Type: string // Required: No // Stability: stable // Examples: '0.9.1' MessagingProtocolVersionKey = attribute.Key("messaging.protocol_version") // Connection string. // // Type: string // Required: No // Stability: stable // Examples: 'tibjmsnaming://localhost:7222', // 'https://queue.amazonaws.com/80398EXAMPLE/MyQueue' MessagingURLKey = attribute.Key("messaging.url") // A value used by the messaging system as an identifier for the message, // represented as a string. // // Type: string // Required: No // Stability: stable // Examples: '452a7c7c7c7048c2f887f61572b18fc2' MessagingMessageIDKey = attribute.Key("messaging.message_id") // The [conversation ID](#conversations) identifying the conversation to which the // message belongs, represented as a string. Sometimes called "Correlation ID". // // Type: string // Required: No // Stability: stable // Examples: 'MyConversationID' MessagingConversationIDKey = attribute.Key("messaging.conversation_id") // The (uncompressed) size of the message payload in bytes. Also use this // attribute if it is unknown whether the compressed or uncompressed payload size // is reported. // // Type: int // Required: No // Stability: stable // Examples: 2738 MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message_payload_size_bytes") // The compressed size of the message payload in bytes. // // Type: int // Required: No // Stability: stable // Examples: 2048 MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message_payload_compressed_size_bytes") ) var ( // A message sent to a queue MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") // A message sent to a topic MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") ) // Semantic convention for a consumer of messages received from a messaging system const ( // A string identifying the kind of message consumption as defined in the // [Operation names](#operation-names) section above. If the operation is "send", // this attribute MUST NOT be set, since the operation can be inferred from the // span kind in that case. // // Type: Enum // Required: No // Stability: stable MessagingOperationKey = attribute.Key("messaging.operation") // The identifier for the consumer receiving a message. For Kafka, set it to // `{messaging.kafka.consumer_group} - {messaging.kafka.client_id}`, if both are // present, or only `messaging.kafka.consumer_group`. For brokers, such as // RabbitMQ and Artemis, set it to the `client_id` of the client consuming the // message. // // Type: string // Required: No // Stability: stable // Examples: 'mygroup - client-6' MessagingConsumerIDKey = attribute.Key("messaging.consumer_id") ) var ( // receive MessagingOperationReceive = MessagingOperationKey.String("receive") // process MessagingOperationProcess = MessagingOperationKey.String("process") ) // Attributes for RabbitMQ const ( // RabbitMQ message routing key. // // Type: string // Required: Unless it is empty. // Stability: stable // Examples: 'myKey' MessagingRabbitmqRoutingKeyKey = attribute.Key("messaging.rabbitmq.routing_key") ) // Attributes for Apache Kafka const ( // Message keys in Kafka are used for grouping alike messages to ensure they're // processed on the same partition. They differ from `messaging.message_id` in // that they're not unique. If the key is `null`, the attribute MUST NOT be set. // // Type: string // Required: No // Stability: stable // Examples: 'myKey' // Note: If the key type is not string, it's string representation has to be // supplied for the attribute. If the key has no unambiguous, canonical string // form, don't include its value. MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message_key") // Name of the Kafka Consumer Group that is handling the message. Only applies to // consumers, not producers. // // Type: string // Required: No // Stability: stable // Examples: 'my-group' MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer_group") // Client ID for the Consumer or Producer that is handling the message. // // Type: string // Required: No // Stability: stable // Examples: 'client-5' MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") // Partition the message is sent to. // // Type: int // Required: No // Stability: stable // Examples: 2 MessagingKafkaPartitionKey = attribute.Key("messaging.kafka.partition") // A boolean that is true if the message is a tombstone. // // Type: boolean // Required: If missing, it is assumed to be false. // Stability: stable MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") ) // This document defines semantic conventions for remote procedure calls. const ( // A string identifying the remoting system. // // Type: string // Required: Always // Stability: stable // Examples: 'grpc', 'java_rmi', 'wcf' RPCSystemKey = attribute.Key("rpc.system") // The full (logical) name of the service being called, including its package // name, if applicable. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'myservice.EchoService' // Note: This is the logical name of the service from the RPC interface // perspective, which can be different from the name of any implementing class. // The `code.namespace` attribute may be used to store the latter (despite the // attribute name, it may include a class name; e.g., class with method actually // executing the call on the server side, RPC client stub class on the client // side). RPCServiceKey = attribute.Key("rpc.service") // The name of the (logical) method being called, must be equal to the $method // part in the span name. // // Type: string // Required: No, but recommended // Stability: stable // Examples: 'exampleMethod' // Note: This is the logical name of the method from the RPC interface // perspective, which can be different from the name of any implementing // method/function. The `code.function` attribute may be used to store the latter // (e.g., method actually executing the call on the server side, RPC client stub // method on the client side). RPCMethodKey = attribute.Key("rpc.method") ) // Tech-specific attributes for gRPC. const ( // The [numeric status // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC // request. // // Type: Enum // Required: Always // Stability: stable RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) var ( // OK RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) // CANCELLED RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) // UNKNOWN RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) // INVALID_ARGUMENT RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) // DEADLINE_EXCEEDED RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) // NOT_FOUND RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) // ALREADY_EXISTS RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) // PERMISSION_DENIED RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) // RESOURCE_EXHAUSTED RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) // FAILED_PRECONDITION RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) // ABORTED RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) // OUT_OF_RANGE RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) // UNIMPLEMENTED RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) // INTERNAL RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) // UNAVAILABLE RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) // DATA_LOSS RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) // UNAUTHENTICATED RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) ) // Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). const ( // Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC // 1.0 does not specify this, the value can be omitted. // // Type: string // Required: If missing, it is assumed to be "1.0". // Stability: stable // Examples: '2.0', '1.0' RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") // `id` property of request or response. Since protocol allows id to be int, // string, `null` or missing (for notifications), value is expected to be cast to // string for simplicity. Use empty string in case of `null` value. Omit entirely // if this is a notification. // // Type: string // Required: No // Stability: stable // Examples: '10', 'request-7', '' RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") // `error.code` property of response if it is an error response. // // Type: int // Required: If missing, response is assumed to be successful. // Stability: stable // Examples: -32700, 100 RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") // `error.message` property of response if it is an error response. // // Type: string // Required: No // Stability: stable // Examples: 'Parse error', 'User already exists' RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") ) // RPC received/sent message. const ( // Whether this is a received or sent message. // // Type: Enum // Required: No // Stability: stable MessageTypeKey = attribute.Key("message.type") // MUST be calculated as two different counters starting from `1` one for sent // messages and one for received message. // // Type: int // Required: No // Stability: stable // Note: This way we guarantee that the values will be consistent between // different implementations. MessageIDKey = attribute.Key("message.id") // Compressed size of the message in bytes. // // Type: int // Required: No // Stability: stable MessageCompressedSizeKey = attribute.Key("message.compressed_size") // Uncompressed size of the message in bytes. // // Type: int // Required: No // Stability: stable MessageUncompressedSizeKey = attribute.Key("message.uncompressed_size") ) var ( // sent MessageTypeSent = MessageTypeKey.String("SENT") // received MessageTypeReceived = MessageTypeKey.String("RECEIVED") ) golang-opentelemetry-otel-1.1.0/tag.sh000077500000000000000000000103101414226744000177240ustar00rootroot00000000000000#!/usr/bin/env bash # 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. readonly PROGNAME=$(basename "$0") readonly PROGDIR=$(readlink -m "$(dirname "$0")") readonly EXCLUDE_PACKAGES="internal/tools" readonly SEMVER_REGEX="v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?" usage() { cat <<- EOF Usage: $PROGNAME [OPTIONS] SEMVER_TAG COMMIT_HASH Creates git tag for all Go packages in project. OPTIONS: -h --help Show this help. ARGUMENTS: SEMVER_TAG Semantic version to tag with. COMMIT_HASH Git commit hash to tag. EOF } cmdline() { local arg commit for arg do local delim="" case "$arg" in # Translate long form options to short form. --help) args="${args}-h ";; # Pass through for everything else. *) [[ "${arg:0:1}" == "-" ]] || delim="\"" args="${args}${delim}${arg}${delim} ";; esac done # Reset and process short form options. eval set -- "$args" while getopts "h" OPTION do case $OPTION in h) usage exit 0 ;; *) echo "unknown option: $OPTION" usage exit 1 ;; esac done # Positional arguments. shift $((OPTIND-1)) readonly TAG="$1" if [ -z "$TAG" ] then echo "missing SEMVER_TAG" usage exit 1 fi if [[ ! "$TAG" =~ $SEMVER_REGEX ]] then printf "invalid semantic version: %s\n" "$TAG" exit 2 fi if [[ "$( git tag --list "$TAG" )" ]] then printf "tag already exists: %s\n" "$TAG" exit 2 fi shift commit="$1" if [ -z "$commit" ] then echo "missing COMMIT_HASH" usage exit 1 fi # Verify rev is for a commit and unify hashes into a complete SHA1. readonly SHA="$( git rev-parse --quiet --verify "${commit}^{commit}" )" if [ -z "$SHA" ] then printf "invalid commit hash: %s\n" "$commit" exit 2 fi if [ "$( git merge-base "$SHA" HEAD )" != "$SHA" ] then printf "commit '%s' not found on this branch\n" "$commit" exit 2 fi } package_dirs() { # Return a list of package directories in the form: # # package/directory/a # package/directory/b # deeper/package/directory/a # ... # # Making sure to exclude any packages in the EXCLUDE_PACKAGES regexp. find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \ | grep -E -v "$EXCLUDE_PACKAGES" \ | sed 's/^\.\///' \ | sort } git_tag() { local tag="$1" local commit="$2" git tag -a "$tag" -s -m "Version $tag" "$commit" } previous_version() { local current="$1" # Requires git > 2.0 git tag -l --sort=v:refname \ | grep -E "^${SEMVER_REGEX}$" \ | grep -v "$current" \ | tail -1 } print_changes() { local tag="$1" local previous previous="$( previous_version "$tag" )" if [ -n "$previous" ] then printf "\nRaw changes made between %s and %s\n" "$previous" "$tag" printf "======================================\n" git --no-pager log --pretty=oneline "${previous}..$tag" fi } main() { local dir cmdline "$@" cd "$PROGDIR" || exit 3 # Create tag for root package. git_tag "$TAG" "$SHA" printf "created tag: %s\n" "$TAG" # Create tag for all sub-packages. for dir in $( package_dirs ) do git_tag "${dir}/$TAG" "$SHA" printf "created tag: %s\n" "${dir}/$TAG" done print_changes "$TAG" } main "$@" golang-opentelemetry-otel-1.1.0/trace.go000066400000000000000000000031211414226744000202410ustar00rootroot00000000000000// 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 otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/trace" ) // Tracer creates a named tracer that implements Tracer interface. // If the name is an empty string then provider uses default name. // // This is short for GetTracerProvider().Tracer(name, opts...) func Tracer(name string, opts ...trace.TracerOption) trace.Tracer { return GetTracerProvider().Tracer(name, opts...) } // GetTracerProvider returns the registered global trace provider. // If none is registered then an instance of NoopTracerProvider is returned. // // Use the trace provider to create a named tracer. E.g. // tracer := otel.GetTracerProvider().Tracer("example.com/foo") // or // tracer := otel.Tracer("example.com/foo") func GetTracerProvider() trace.TracerProvider { return global.TracerProvider() } // SetTracerProvider registers `tp` as the global trace provider. func SetTracerProvider(tp trace.TracerProvider) { global.SetTracerProvider(tp) } golang-opentelemetry-otel-1.1.0/trace/000077500000000000000000000000001414226744000177155ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/trace/config.go000066400000000000000000000215271414226744000215200ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import ( "time" "go.opentelemetry.io/otel/attribute" ) // TracerConfig is a group of options for a Tracer. type TracerConfig struct { instrumentationVersion string // Schema URL of the telemetry emitted by the Tracer. schemaURL string } // InstrumentationVersion returns the version of the library providing instrumentation. func (t *TracerConfig) InstrumentationVersion() string { return t.instrumentationVersion } // SchemaURL returns the Schema URL of the telemetry emitted by the Tracer. func (t *TracerConfig) SchemaURL() string { return t.schemaURL } // NewTracerConfig applies all the options to a returned TracerConfig. func NewTracerConfig(options ...TracerOption) TracerConfig { var config TracerConfig for _, option := range options { option.apply(&config) } return config } // TracerOption applies an option to a TracerConfig. type TracerOption interface { apply(*TracerConfig) } type tracerOptionFunc func(*TracerConfig) func (fn tracerOptionFunc) apply(cfg *TracerConfig) { fn(cfg) } // SpanConfig is a group of options for a Span. type SpanConfig struct { attributes []attribute.KeyValue timestamp time.Time links []Link newRoot bool spanKind SpanKind stackTrace bool } // Attributes describe the associated qualities of a Span. func (cfg *SpanConfig) Attributes() []attribute.KeyValue { return cfg.attributes } // Timestamp is a time in a Span life-cycle. func (cfg *SpanConfig) Timestamp() time.Time { return cfg.timestamp } // StackTrace checks whether stack trace capturing is enabled. func (cfg *SpanConfig) StackTrace() bool { return cfg.stackTrace } // Links are the associations a Span has with other Spans. func (cfg *SpanConfig) Links() []Link { return cfg.links } // NewRoot identifies a Span as the root Span for a new trace. This is // commonly used when an existing trace crosses trust boundaries and the // remote parent span context should be ignored for security. func (cfg *SpanConfig) NewRoot() bool { return cfg.newRoot } // SpanKind is the role a Span has in a trace. func (cfg *SpanConfig) SpanKind() SpanKind { return cfg.spanKind } // NewSpanStartConfig applies all the options to a returned SpanConfig. // No validation is performed on the returned SpanConfig (e.g. no uniqueness // checking or bounding of data), it is left to the SDK to perform this // action. func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { var c SpanConfig for _, option := range options { option.applySpanStart(&c) } return c } // NewSpanEndConfig applies all the options to a returned SpanConfig. // No validation is performed on the returned SpanConfig (e.g. no uniqueness // checking or bounding of data), it is left to the SDK to perform this // action. func NewSpanEndConfig(options ...SpanEndOption) SpanConfig { var c SpanConfig for _, option := range options { option.applySpanEnd(&c) } return c } // SpanStartOption applies an option to a SpanConfig. These options are applicable // only when the span is created type SpanStartOption interface { applySpanStart(*SpanConfig) } type spanOptionFunc func(*SpanConfig) func (fn spanOptionFunc) applySpanStart(cfg *SpanConfig) { fn(cfg) } // SpanEndOption applies an option to a SpanConfig. These options are // applicable only when the span is ended. type SpanEndOption interface { applySpanEnd(*SpanConfig) } // EventConfig is a group of options for an Event. type EventConfig struct { attributes []attribute.KeyValue timestamp time.Time stackTrace bool } // Attributes describe the associated qualities of an Event. func (cfg *EventConfig) Attributes() []attribute.KeyValue { return cfg.attributes } // Timestamp is a time in an Event life-cycle. func (cfg *EventConfig) Timestamp() time.Time { return cfg.timestamp } // StackTrace checks whether stack trace capturing is enabled. func (cfg *EventConfig) StackTrace() bool { return cfg.stackTrace } // NewEventConfig applies all the EventOptions to a returned EventConfig. If no // timestamp option is passed, the returned EventConfig will have a Timestamp // set to the call time, otherwise no validation is performed on the returned // EventConfig. func NewEventConfig(options ...EventOption) EventConfig { var c EventConfig for _, option := range options { option.applyEvent(&c) } if c.timestamp.IsZero() { c.timestamp = time.Now() } return c } // EventOption applies span event options to an EventConfig. type EventOption interface { applyEvent(*EventConfig) } // SpanOption are options that can be used at both the beginning and end of a span. type SpanOption interface { SpanStartOption SpanEndOption } // SpanStartEventOption are options that can be used at the start of a span, or with an event. type SpanStartEventOption interface { SpanStartOption EventOption } // SpanEndEventOption are options that can be used at the end of a span, or with an event. type SpanEndEventOption interface { SpanEndOption EventOption } type attributeOption []attribute.KeyValue func (o attributeOption) applySpan(c *SpanConfig) { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) } func (o attributeOption) applySpanStart(c *SpanConfig) { o.applySpan(c) } func (o attributeOption) applyEvent(c *EventConfig) { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) } var _ SpanStartEventOption = attributeOption{} // WithAttributes adds the attributes related to a span life-cycle event. // These attributes are used to describe the work a Span represents when this // option is provided to a Span's start or end events. Otherwise, these // attributes provide additional information about the event being recorded // (e.g. error, state change, processing progress, system event). // // If multiple of these options are passed the attributes of each successive // option will extend the attributes instead of overwriting. There is no // guarantee of uniqueness in the resulting attributes. func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption { return attributeOption(attributes) } // SpanEventOption are options that can be used with an event or a span. type SpanEventOption interface { SpanOption EventOption } type timestampOption time.Time func (o timestampOption) applySpan(c *SpanConfig) { c.timestamp = time.Time(o) } func (o timestampOption) applySpanStart(c *SpanConfig) { o.applySpan(c) } func (o timestampOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) } func (o timestampOption) applyEvent(c *EventConfig) { c.timestamp = time.Time(o) } var _ SpanEventOption = timestampOption{} // WithTimestamp sets the time of a Span or Event life-cycle moment (e.g. // started, stopped, errored). func WithTimestamp(t time.Time) SpanEventOption { return timestampOption(t) } type stackTraceOption bool func (o stackTraceOption) applyEvent(c *EventConfig) { c.stackTrace = bool(o) } func (o stackTraceOption) applySpan(c *SpanConfig) { c.stackTrace = bool(o) } func (o stackTraceOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) } // WithStackTrace sets the flag to capture the error with stack trace (e.g. true, false). func WithStackTrace(b bool) SpanEndEventOption { return stackTraceOption(b) } // WithLinks adds links to a Span. The links are added to the existing Span // links, i.e. this does not overwrite. Links with invalid span context are ignored. func WithLinks(links ...Link) SpanStartOption { return spanOptionFunc(func(cfg *SpanConfig) { cfg.links = append(cfg.links, links...) }) } // WithNewRoot specifies that the Span should be treated as a root Span. Any // existing parent span context will be ignored when defining the Span's trace // identifiers. func WithNewRoot() SpanStartOption { return spanOptionFunc(func(cfg *SpanConfig) { cfg.newRoot = true }) } // WithSpanKind sets the SpanKind of a Span. func WithSpanKind(kind SpanKind) SpanStartOption { return spanOptionFunc(func(cfg *SpanConfig) { cfg.spanKind = kind }) } // WithInstrumentationVersion sets the instrumentation version. func WithInstrumentationVersion(version string) TracerOption { return tracerOptionFunc(func(cfg *TracerConfig) { cfg.instrumentationVersion = version }) } // WithSchemaURL sets the schema URL for the Tracer. func WithSchemaURL(schemaURL string) TracerOption { return tracerOptionFunc(func(cfg *TracerConfig) { cfg.schemaURL = schemaURL }) } golang-opentelemetry-otel-1.1.0/trace/config_test.go000066400000000000000000000121161414226744000225510ustar00rootroot00000000000000// 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 trace import ( "testing" "time" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" ) func TestNewSpanConfig(t *testing.T) { k1v1 := attribute.String("key1", "value1") k1v2 := attribute.String("key1", "value2") k2v2 := attribute.String("key2", "value2") timestamp0 := time.Unix(0, 0) timestamp1 := time.Unix(0, 0) link1 := Link{ SpanContext: SpanContext{traceID: TraceID([16]byte{1, 1}), spanID: SpanID{3}}, Attributes: []attribute.KeyValue{k1v1}, } link2 := Link{ SpanContext: SpanContext{traceID: TraceID([16]byte{1, 1}), spanID: SpanID{3}}, Attributes: []attribute.KeyValue{k1v2, k2v2}, } tests := []struct { options []SpanStartOption expected SpanConfig }{ { // No non-zero-values should be set. []SpanStartOption{}, SpanConfig{}, }, { []SpanStartOption{ WithAttributes(k1v1), }, SpanConfig{ attributes: []attribute.KeyValue{k1v1}, }, }, { // Multiple calls should append not overwrite. []SpanStartOption{ WithAttributes(k1v1), WithAttributes(k1v2), WithAttributes(k2v2), }, SpanConfig{ // No uniqueness is guaranteed by the API. attributes: []attribute.KeyValue{k1v1, k1v2, k2v2}, }, }, { []SpanStartOption{ WithAttributes(k1v1, k1v2, k2v2), }, SpanConfig{ // No uniqueness is guaranteed by the API. attributes: []attribute.KeyValue{k1v1, k1v2, k2v2}, }, }, { []SpanStartOption{ WithTimestamp(timestamp0), }, SpanConfig{ timestamp: timestamp0, }, }, { []SpanStartOption{ // Multiple calls overwrites with last-one-wins. WithTimestamp(timestamp0), WithTimestamp(timestamp1), }, SpanConfig{ timestamp: timestamp1, }, }, { []SpanStartOption{ WithLinks(link1), }, SpanConfig{ links: []Link{link1}, }, }, { []SpanStartOption{ // Multiple calls should append not overwrite. WithLinks(link1), WithLinks(link1, link2), }, SpanConfig{ // No uniqueness is guaranteed by the API. links: []Link{link1, link1, link2}, }, }, { []SpanStartOption{ WithNewRoot(), }, SpanConfig{ newRoot: true, }, }, { []SpanStartOption{ // Multiple calls should not change NewRoot state. WithNewRoot(), WithNewRoot(), }, SpanConfig{ newRoot: true, }, }, { []SpanStartOption{ WithSpanKind(SpanKindConsumer), }, SpanConfig{ spanKind: SpanKindConsumer, }, }, { []SpanStartOption{ // Multiple calls overwrites with last-one-wins. WithSpanKind(SpanKindClient), WithSpanKind(SpanKindConsumer), }, SpanConfig{ spanKind: SpanKindConsumer, }, }, { // Everything should work together. []SpanStartOption{ WithAttributes(k1v1), WithTimestamp(timestamp0), WithLinks(link1, link2), WithNewRoot(), WithSpanKind(SpanKindConsumer), }, SpanConfig{ attributes: []attribute.KeyValue{k1v1}, timestamp: timestamp0, links: []Link{link1, link2}, newRoot: true, spanKind: SpanKindConsumer, }, }, } for _, test := range tests { assert.Equal(t, test.expected, NewSpanStartConfig(test.options...)) } } func TestEndSpanConfig(t *testing.T) { timestamp := time.Unix(0, 0) tests := []struct { options []SpanEndOption expected SpanConfig }{ { []SpanEndOption{}, SpanConfig{}, }, { []SpanEndOption{ WithStackTrace(true), }, SpanConfig{ stackTrace: true, }, }, { []SpanEndOption{ WithTimestamp(timestamp), }, SpanConfig{ timestamp: timestamp, }, }, } for _, test := range tests { assert.Equal(t, test.expected, NewSpanEndConfig(test.options...)) } } func TestTracerConfig(t *testing.T) { v1 := "semver:0.0.1" v2 := "semver:1.0.0" schemaURL := "https://opentelemetry.io/schemas/1.2.0" tests := []struct { options []TracerOption expected TracerConfig }{ { // No non-zero-values should be set. []TracerOption{}, TracerConfig{}, }, { []TracerOption{ WithInstrumentationVersion(v1), }, TracerConfig{ instrumentationVersion: v1, }, }, { []TracerOption{ // Multiple calls should overwrite. WithInstrumentationVersion(v1), WithInstrumentationVersion(v2), }, TracerConfig{ instrumentationVersion: v2, }, }, { []TracerOption{ WithSchemaURL(schemaURL), }, TracerConfig{ schemaURL: schemaURL, }, }, } for _, test := range tests { config := NewTracerConfig(test.options...) assert.Equal(t, test.expected, config) } } golang-opentelemetry-otel-1.1.0/trace/context.go000066400000000000000000000044311414226744000217320ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import "context" type traceContextKeyType int const currentSpanKey traceContextKeyType = iota // ContextWithSpan returns a copy of parent with span set as the current Span. func ContextWithSpan(parent context.Context, span Span) context.Context { return context.WithValue(parent, currentSpanKey, span) } // ContextWithSpanContext returns a copy of parent with sc as the current // Span. The Span implementation that wraps sc is non-recording and performs // no operations other than to return sc as the SpanContext from the // SpanContext method. func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Context { return ContextWithSpan(parent, nonRecordingSpan{sc: sc}) } // ContextWithRemoteSpanContext returns a copy of parent with rsc set explicly // as a remote SpanContext and as the current Span. The Span implementation // that wraps rsc is non-recording and performs no operations other than to // return rsc as the SpanContext from the SpanContext method. func ContextWithRemoteSpanContext(parent context.Context, rsc SpanContext) context.Context { return ContextWithSpanContext(parent, rsc.WithRemote(true)) } // SpanFromContext returns the current Span from ctx. // // If no Span is currently set in ctx an implementation of a Span that // performs no operations is returned. func SpanFromContext(ctx context.Context) Span { if ctx == nil { return noopSpan{} } if span, ok := ctx.Value(currentSpanKey).(Span); ok { return span } return noopSpan{} } // SpanContextFromContext returns the current Span's SpanContext. func SpanContextFromContext(ctx context.Context) SpanContext { return SpanFromContext(ctx).SpanContext() } golang-opentelemetry-otel-1.1.0/trace/context_test.go000066400000000000000000000045771414226744000230040ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import ( "context" "testing" "github.com/stretchr/testify/assert" ) type testSpan struct { noopSpan ID byte Remote bool } func (s testSpan) SpanContext() SpanContext { return SpanContext{ traceID: [16]byte{1}, spanID: [8]byte{s.ID}, remote: s.Remote, } } var ( emptySpan = noopSpan{} localSpan = testSpan{ID: 1, Remote: false} remoteSpan = testSpan{ID: 1, Remote: true} wrappedSpan = nonRecordingSpan{sc: remoteSpan.SpanContext()} ) func TestSpanFromContext(t *testing.T) { testCases := []struct { name string context context.Context expectedSpan Span }{ { name: "empty context", context: nil, expectedSpan: emptySpan, }, { name: "background context", context: context.Background(), expectedSpan: emptySpan, }, { name: "local span", context: ContextWithSpan(context.Background(), localSpan), expectedSpan: localSpan, }, { name: "remote span", context: ContextWithSpan(context.Background(), remoteSpan), expectedSpan: remoteSpan, }, { name: "wrapped remote span", context: ContextWithRemoteSpanContext(context.Background(), remoteSpan.SpanContext()), expectedSpan: wrappedSpan, }, { name: "wrapped local span becomes remote", context: ContextWithRemoteSpanContext(context.Background(), localSpan.SpanContext()), expectedSpan: wrappedSpan, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expectedSpan, SpanFromContext(tc.context)) // Ensure SpanContextFromContext is just // SpanFromContext(…).SpanContext(). assert.Equal(t, tc.expectedSpan.SpanContext(), SpanContextFromContext(tc.context)) }) } } golang-opentelemetry-otel-1.1.0/trace/doc.go000066400000000000000000000036541414226744000210210ustar00rootroot00000000000000// 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 trace provides an implementation of the tracing part of the OpenTelemetry API. To participate in distributed traces a Span needs to be created for the operation being performed as part of a traced workflow. It its simplest form: var tracer trace.Tracer func init() { tracer = otel.Tracer("instrumentation/package/name") } func operation(ctx context.Context) { var span trace.Span ctx, span = tracer.Start(ctx, "operation") defer span.End() // ... } A Tracer is unique to the instrumentation and is used to create Spans. Instrumentation should be designed to accept a TracerProvider from which it can create its own unique Tracer. Alternatively, the registered global TracerProvider from the go.opentelemetry.io/otel package can be used as a default. const ( name = "instrumentation/package/name" version = "0.1.0" ) type Instrumentation struct { tracer trace.Tracer } func NewInstrumentation(tp trace.TracerProvider) *Instrumentation { if tp == nil { tp = otel.TracerProvider() } return &Instrumentation{ tracer: tp.Tracer(name, trace.WithInstrumentationVersion(version)), } } func operation(ctx context.Context, inst *Instrumentation) { var span trace.Span ctx, span = inst.tracer.Start(ctx, "operation") defer span.End() // ... } */ package trace // import "go.opentelemetry.io/otel/trace" golang-opentelemetry-otel-1.1.0/trace/go.mod000066400000000000000000000052231414226744000210250ustar00rootroot00000000000000module go.opentelemetry.io/otel/trace go 1.15 replace go.opentelemetry.io/otel => ../ replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opentracing => ../bridge/opentracing replace go.opentelemetry.io/otel/example/jaeger => ../example/jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../example/namedtracer replace go.opentelemetry.io/otel/example/opencensus => ../example/opencensus replace go.opentelemetry.io/otel/example/otel-collector => ../example/otel-collector replace go.opentelemetry.io/otel/example/prom-collector => ../example/prom-collector replace go.opentelemetry.io/otel/example/prometheus => ../example/prometheus replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/prometheus => ../exporters/prometheus replace go.opentelemetry.io/otel/exporters/jaeger => ../exporters/jaeger replace go.opentelemetry.io/otel/exporters/zipkin => ../exporters/zipkin replace go.opentelemetry.io/otel/internal/tools => ../internal/tools replace go.opentelemetry.io/otel/metric => ../metric replace go.opentelemetry.io/otel/sdk => ../sdk replace go.opentelemetry.io/otel/sdk/export/metric => ../sdk/export/metric replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric replace go.opentelemetry.io/otel/trace => ./ require ( github.com/google/go-cmp v0.5.6 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v1.1.0 ) replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/internal/metric => ../internal/metric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test replace go.opentelemetry.io/otel/example/fib => ../example/fib replace go.opentelemetry.io/otel/schema => ../schema golang-opentelemetry-otel-1.1.0/trace/go.sum000066400000000000000000000025761414226744000210620ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-opentelemetry-otel-1.1.0/trace/nonrecording.go000066400000000000000000000017431414226744000227400ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" // nonRecordingSpan is a minimal implementation of a Span that wraps a // SpanContext. It performs no operations other than to return the wrapped // SpanContext. type nonRecordingSpan struct { noopSpan sc SpanContext } // SpanContext returns the wrapped SpanContext. func (s nonRecordingSpan) SpanContext() SpanContext { return s.sc } golang-opentelemetry-otel-1.1.0/trace/noop.go000066400000000000000000000053151414226744000212230ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import ( "context" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // NewNoopTracerProvider returns an implementation of TracerProvider that // performs no operations. The Tracer and Spans created from the returned // TracerProvider also perform no operations. func NewNoopTracerProvider() TracerProvider { return noopTracerProvider{} } type noopTracerProvider struct{} var _ TracerProvider = noopTracerProvider{} // Tracer returns noop implementation of Tracer. func (p noopTracerProvider) Tracer(string, ...TracerOption) Tracer { return noopTracer{} } // noopTracer is an implementation of Tracer that preforms no operations. type noopTracer struct{} var _ Tracer = noopTracer{} // Start carries forward a non-recording Span, if one is present in the context, otherwise it // creates a no-op Span. func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanStartOption) (context.Context, Span) { span := SpanFromContext(ctx) if _, ok := span.(nonRecordingSpan); !ok { // span is likely already a noopSpan, but let's be sure span = noopSpan{} } return ContextWithSpan(ctx, span), span } // noopSpan is an implementation of Span that preforms no operations. type noopSpan struct{} var _ Span = noopSpan{} // SpanContext returns an empty span context. func (noopSpan) SpanContext() SpanContext { return SpanContext{} } // IsRecording always returns false. func (noopSpan) IsRecording() bool { return false } // SetStatus does nothing. func (noopSpan) SetStatus(codes.Code, string) {} // SetError does nothing. func (noopSpan) SetError(bool) {} // SetAttributes does nothing. func (noopSpan) SetAttributes(...attribute.KeyValue) {} // End does nothing. func (noopSpan) End(...SpanEndOption) {} // RecordError does nothing. func (noopSpan) RecordError(error, ...EventOption) {} // AddEvent does nothing. func (noopSpan) AddEvent(string, ...EventOption) {} // SetName does nothing. func (noopSpan) SetName(string) {} // TracerProvider returns a no-op TracerProvider func (noopSpan) TracerProvider() TracerProvider { return noopTracerProvider{} } golang-opentelemetry-otel-1.1.0/trace/noop_test.go000066400000000000000000000055451414226744000222670ustar00rootroot00000000000000// 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 trace import ( "context" "testing" ) func TestNewNoopTracerProvider(t *testing.T) { got, want := NewNoopTracerProvider(), noopTracerProvider{} if got != want { t.Errorf("NewNoopTracerProvider() returned %#v, want %#v", got, want) } } func TestNoopTracerProviderTracer(t *testing.T) { tp := NewNoopTracerProvider() got, want := tp.Tracer(""), noopTracer{} if got != want { t.Errorf("noopTracerProvider.Tracer() returned %#v, want %#v", got, want) } } func TestNoopTracerStart(t *testing.T) { ctx := context.Background() tracer := NewNoopTracerProvider().Tracer("test instrumentation") var span Span ctx, span = tracer.Start(ctx, "span name") got, ok := span.(noopSpan) if !ok { t.Fatalf("noopTracer.Start() returned a non-noopSpan: %#v", span) } want := noopSpan{} if got != want { t.Errorf("noopTracer.Start() returned %#v, want %#v", got, want) } got, ok = SpanFromContext(ctx).(noopSpan) if !ok { t.Fatal("noopTracer.Start() did not set span as current in returned context") } if got != want { t.Errorf("noopTracer.Start() current span in returned context set to %#v, want %#v", got, want) } } func TestNoopSpan(t *testing.T) { tracer := NewNoopTracerProvider().Tracer("test instrumentation") _, s := tracer.Start(context.Background(), "test span") span := s.(noopSpan) if got, want := span.SpanContext(), (SpanContext{}); !assertSpanContextEqual(got, want) { t.Errorf("span.SpanContext() returned %#v, want %#v", got, want) } if got, want := span.IsRecording(), false; got != want { t.Errorf("span.IsRecording() returned %#v, want %#v", got, want) } } func TestNonRecordingSpanTracerStart(t *testing.T) { tid, err := TraceIDFromHex("01000000000000000000000000000000") if err != nil { t.Fatalf("failure creating TraceID: %s", err.Error()) } sid, err := SpanIDFromHex("0200000000000000") if err != nil { t.Fatalf("failure creating SpanID: %s", err.Error()) } sc := NewSpanContext(SpanContextConfig{TraceID: tid, SpanID: sid}) ctx := ContextWithSpanContext(context.Background(), sc) _, span := NewNoopTracerProvider().Tracer("test instrumentation").Start(ctx, "span1") if got, want := span.SpanContext(), sc; !assertSpanContextEqual(got, want) { t.Errorf("SpanContext not carried by nonRecordingSpan. got %#v, want %#v", got, want) } } golang-opentelemetry-otel-1.1.0/trace/trace.go000066400000000000000000000405371414226744000213530ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import ( "bytes" "context" "encoding/hex" "encoding/json" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) const ( // FlagsSampled is a bitmask with the sampled bit set. A SpanContext // with the sampling bit set means the span is sampled. FlagsSampled = TraceFlags(0x01) errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase" errInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32" errNilTraceID errorConst = "trace-id can't be all zero" errInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16" errNilSpanID errorConst = "span-id can't be all zero" ) type errorConst string func (e errorConst) Error() string { return string(e) } // TraceID is a unique identity of a trace. // nolint:revive // revive complains about stutter of `trace.TraceID`. type TraceID [16]byte var nilTraceID TraceID var _ json.Marshaler = nilTraceID // IsValid checks whether the trace TraceID is valid. A valid trace ID does // not consist of zeros only. func (t TraceID) IsValid() bool { return !bytes.Equal(t[:], nilTraceID[:]) } // MarshalJSON implements a custom marshal function to encode TraceID // as a hex string. func (t TraceID) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } // String returns the hex string representation form of a TraceID func (t TraceID) String() string { return hex.EncodeToString(t[:]) } // SpanID is a unique identity of a span in a trace. type SpanID [8]byte var nilSpanID SpanID var _ json.Marshaler = nilSpanID // IsValid checks whether the SpanID is valid. A valid SpanID does not consist // of zeros only. func (s SpanID) IsValid() bool { return !bytes.Equal(s[:], nilSpanID[:]) } // MarshalJSON implements a custom marshal function to encode SpanID // as a hex string. func (s SpanID) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } // String returns the hex string representation form of a SpanID func (s SpanID) String() string { return hex.EncodeToString(s[:]) } // TraceIDFromHex returns a TraceID from a hex string if it is compliant with // the W3C trace-context specification. See more at // https://www.w3.org/TR/trace-context/#trace-id // nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`. func TraceIDFromHex(h string) (TraceID, error) { t := TraceID{} if len(h) != 32 { return t, errInvalidTraceIDLength } if err := decodeHex(h, t[:]); err != nil { return t, err } if !t.IsValid() { return t, errNilTraceID } return t, nil } // SpanIDFromHex returns a SpanID from a hex string if it is compliant // with the w3c trace-context specification. // See more at https://www.w3.org/TR/trace-context/#parent-id func SpanIDFromHex(h string) (SpanID, error) { s := SpanID{} if len(h) != 16 { return s, errInvalidSpanIDLength } if err := decodeHex(h, s[:]); err != nil { return s, err } if !s.IsValid() { return s, errNilSpanID } return s, nil } func decodeHex(h string, b []byte) error { for _, r := range h { switch { case 'a' <= r && r <= 'f': continue case '0' <= r && r <= '9': continue default: return errInvalidHexID } } decoded, err := hex.DecodeString(h) if err != nil { return err } copy(b, decoded) return nil } // TraceFlags contains flags that can be set on a SpanContext type TraceFlags byte //nolint:revive // revive complains about stutter of `trace.TraceFlags`. // IsSampled returns if the sampling bit is set in the TraceFlags. func (tf TraceFlags) IsSampled() bool { return tf&FlagsSampled == FlagsSampled } // WithSampled sets the sampling bit in a new copy of the TraceFlags. func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { if sampled { return tf | FlagsSampled } return tf &^ FlagsSampled } // MarshalJSON implements a custom marshal function to encode TraceFlags // as a hex string. func (tf TraceFlags) MarshalJSON() ([]byte, error) { return json.Marshal(tf.String()) } // String returns the hex string representation form of TraceFlags func (tf TraceFlags) String() string { return hex.EncodeToString([]byte{byte(tf)}[:]) } // SpanContextConfig contains mutable fields usable for constructing // an immutable SpanContext. type SpanContextConfig struct { TraceID TraceID SpanID SpanID TraceFlags TraceFlags TraceState TraceState Remote bool } // NewSpanContext constructs a SpanContext using values from the provided // SpanContextConfig. func NewSpanContext(config SpanContextConfig) SpanContext { return SpanContext{ traceID: config.TraceID, spanID: config.SpanID, traceFlags: config.TraceFlags, traceState: config.TraceState, remote: config.Remote, } } // SpanContext contains identifying trace information about a Span. type SpanContext struct { traceID TraceID spanID SpanID traceFlags TraceFlags traceState TraceState remote bool } var _ json.Marshaler = SpanContext{} // IsValid returns if the SpanContext is valid. A valid span context has a // valid TraceID and SpanID. func (sc SpanContext) IsValid() bool { return sc.HasTraceID() && sc.HasSpanID() } // IsRemote indicates whether the SpanContext represents a remotely-created Span. func (sc SpanContext) IsRemote() bool { return sc.remote } // WithRemote returns a copy of sc with the Remote property set to remote. func (sc SpanContext) WithRemote(remote bool) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: remote, } } // TraceID returns the TraceID from the SpanContext. func (sc SpanContext) TraceID() TraceID { return sc.traceID } // HasTraceID checks if the SpanContext has a valid TraceID. func (sc SpanContext) HasTraceID() bool { return sc.traceID.IsValid() } // WithTraceID returns a new SpanContext with the TraceID replaced. func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext { return SpanContext{ traceID: traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: sc.remote, } } // SpanID returns the SpanID from the SpanContext. func (sc SpanContext) SpanID() SpanID { return sc.spanID } // HasSpanID checks if the SpanContext has a valid SpanID. func (sc SpanContext) HasSpanID() bool { return sc.spanID.IsValid() } // WithSpanID returns a new SpanContext with the SpanID replaced. func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: sc.remote, } } // TraceFlags returns the flags from the SpanContext. func (sc SpanContext) TraceFlags() TraceFlags { return sc.traceFlags } // IsSampled returns if the sampling bit is set in the SpanContext's TraceFlags. func (sc SpanContext) IsSampled() bool { return sc.traceFlags.IsSampled() } // WithTraceFlags returns a new SpanContext with the TraceFlags replaced. func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: flags, traceState: sc.traceState, remote: sc.remote, } } // TraceState returns the TraceState from the SpanContext. func (sc SpanContext) TraceState() TraceState { return sc.traceState } // WithTraceState returns a new SpanContext with the TraceState replaced. func (sc SpanContext) WithTraceState(state TraceState) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: state, remote: sc.remote, } } // Equal is a predicate that determines whether two SpanContext values are equal. func (sc SpanContext) Equal(other SpanContext) bool { return sc.traceID == other.traceID && sc.spanID == other.spanID && sc.traceFlags == other.traceFlags && sc.traceState.String() == other.traceState.String() && sc.remote == other.remote } // MarshalJSON implements a custom marshal function to encode a SpanContext. func (sc SpanContext) MarshalJSON() ([]byte, error) { return json.Marshal(SpanContextConfig{ TraceID: sc.traceID, SpanID: sc.spanID, TraceFlags: sc.traceFlags, TraceState: sc.traceState, Remote: sc.remote, }) } // Span is the individual component of a trace. It represents a single named // and timed operation of a workflow that is traced. A Tracer is used to // create a Span and it is then up to the operation the Span represents to // properly end the Span when the operation itself ends. // // Warning: methods may be added to this interface in minor releases. type Span interface { // End completes the Span. The Span is considered complete and ready to be // delivered through the rest of the telemetry pipeline after this method // is called. Therefore, updates to the Span are not allowed after this // method has been called. End(options ...SpanEndOption) // AddEvent adds an event with the provided name and options. AddEvent(name string, options ...EventOption) // IsRecording returns the recording state of the Span. It will return // true if the Span is active and events can be recorded. IsRecording() bool // RecordError will record err as an exception span event for this span. An // additional call to SetStatus is required if the Status of the Span should // be set to Error, as this method does not change the Span status. If this // span is not being recorded or err is nil then this method does nothing. RecordError(err error, options ...EventOption) // SpanContext returns the SpanContext of the Span. The returned SpanContext // is usable even after the End method has been called for the Span. SpanContext() SpanContext // SetStatus sets the status of the Span in the form of a code and a // description, overriding previous values set. The description is only // included in a status when the code is for an error. SetStatus(code codes.Code, description string) // SetName sets the Span name. SetName(name string) // SetAttributes sets kv as attributes of the Span. If a key from kv // already exists for an attribute of the Span it will be overwritten with // the value contained in kv. SetAttributes(kv ...attribute.KeyValue) // TracerProvider returns a TracerProvider that can be used to generate // additional Spans on the same telemetry pipeline as the current Span. TracerProvider() TracerProvider } // Link is the relationship between two Spans. The relationship can be within // the same Trace or across different Traces. // // For example, a Link is used in the following situations: // // 1. Batch Processing: A batch of operations may contain operations // associated with one or more traces/spans. Since there can only be one // parent SpanContext, a Link is used to keep reference to the // SpanContext of all operations in the batch. // 2. Public Endpoint: A SpanContext for an in incoming client request on a // public endpoint should be considered untrusted. In such a case, a new // trace with its own identity and sampling decision needs to be created, // but this new trace needs to be related to the original trace in some // form. A Link is used to keep reference to the original SpanContext and // track the relationship. type Link struct { // SpanContext of the linked Span. SpanContext SpanContext // Attributes describe the aspects of the link. Attributes []attribute.KeyValue } // LinkFromContext returns a link encapsulating the SpanContext in the provided ctx. func LinkFromContext(ctx context.Context, attrs ...attribute.KeyValue) Link { return Link{ SpanContext: SpanContextFromContext(ctx), Attributes: attrs, } } // SpanKind is the role a Span plays in a Trace. type SpanKind int // As a convenience, these match the proto definition, see // https://github.com/open-telemetry/opentelemetry-proto/blob/30d237e1ff3ab7aa50e0922b5bebdd93505090af/opentelemetry/proto/trace/v1/trace.proto#L101-L129 // // The unspecified value is not a valid `SpanKind`. Use `ValidateSpanKind()` // to coerce a span kind to a valid value. const ( // SpanKindUnspecified is an unspecified SpanKind and is not a valid // SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal // if it is received. SpanKindUnspecified SpanKind = 0 // SpanKindInternal is a SpanKind for a Span that represents an internal // operation within an application. SpanKindInternal SpanKind = 1 // SpanKindServer is a SpanKind for a Span that represents the operation // of handling a request from a client. SpanKindServer SpanKind = 2 // SpanKindClient is a SpanKind for a Span that represents the operation // of client making a request to a server. SpanKindClient SpanKind = 3 // SpanKindProducer is a SpanKind for a Span that represents the operation // of a producer sending a message to a message broker. Unlike // SpanKindClient and SpanKindServer, there is often no direct // relationship between this kind of Span and a SpanKindConsumer kind. A // SpanKindProducer Span will end once the message is accepted by the // message broker which might not overlap with the processing of that // message. SpanKindProducer SpanKind = 4 // SpanKindConsumer is a SpanKind for a Span that represents the operation // of a consumer receiving a message from a message broker. Like // SpanKindProducer Spans, there is often no direct relationship between // this Span and the Span that produced the message. SpanKindConsumer SpanKind = 5 ) // ValidateSpanKind returns a valid span kind value. This will coerce // invalid values into the default value, SpanKindInternal. func ValidateSpanKind(spanKind SpanKind) SpanKind { switch spanKind { case SpanKindInternal, SpanKindServer, SpanKindClient, SpanKindProducer, SpanKindConsumer: // valid return spanKind default: return SpanKindInternal } } // String returns the specified name of the SpanKind in lower-case. func (sk SpanKind) String() string { switch sk { case SpanKindInternal: return "internal" case SpanKindServer: return "server" case SpanKindClient: return "client" case SpanKindProducer: return "producer" case SpanKindConsumer: return "consumer" default: return "unspecified" } } // Tracer is the creator of Spans. // // Warning: methods may be added to this interface in minor releases. type Tracer interface { // Start creates a span and a context.Context containing the newly-created span. // // If the context.Context provided in `ctx` contains a Span then the newly-created // Span will be a child of that span, otherwise it will be a root span. This behavior // can be overridden by providing `WithNewRoot()` as a SpanOption, causing the // newly-created Span to be a root span even if `ctx` contains a Span. // // When creating a Span it is recommended to provide all known span attributes using // the `WithAttributes()` SpanOption as samplers will only have access to the // attributes provided when a Span is created. // // Any Span that is created MUST also be ended. This is the responsibility of the user. // Implementations of this API may leak memory or other resources if Spans are not ended. Start(ctx context.Context, spanName string, opts ...SpanStartOption) (context.Context, Span) } // TracerProvider provides access to instrumentation Tracers. // // Warning: methods may be added to this interface in minor releases. type TracerProvider interface { // Tracer creates an implementation of the Tracer interface. // The instrumentationName must be the name of the library providing // instrumentation. This name may be the same as the instrumented code // only if that code provides built-in instrumentation. If the // instrumentationName is empty, then a implementation defined default // name will be used instead. // // This method must be concurrency safe. Tracer(instrumentationName string, opts ...TracerOption) Tracer } golang-opentelemetry-otel-1.1.0/trace/trace_test.go000066400000000000000000000362171414226744000224120ustar00rootroot00000000000000// 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 trace import ( "bytes" "context" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/attribute" "github.com/google/go-cmp/cmp" ) func TestSpanContextIsValid(t *testing.T) { for _, testcase := range []struct { name string tid TraceID sid SpanID want bool }{ { name: "SpanContext.IsValid() returns true if sc has both an Trace ID and Span ID", tid: [16]byte{1}, sid: [8]byte{42}, want: true, }, { name: "SpanContext.IsValid() returns false if sc has neither an Trace ID nor Span ID", tid: TraceID([16]byte{}), sid: [8]byte{}, want: false, }, { name: "SpanContext.IsValid() returns false if sc has a Span ID but not a Trace ID", tid: TraceID([16]byte{}), sid: [8]byte{42}, want: false, }, { name: "SpanContext.IsValid() returns false if sc has a Trace ID but not a Span ID", tid: TraceID([16]byte{1}), sid: [8]byte{}, want: false, }, } { t.Run(testcase.name, func(t *testing.T) { sc := SpanContext{ traceID: testcase.tid, spanID: testcase.sid, } have := sc.IsValid() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestSpanContextEqual(t *testing.T) { a := SpanContext{ traceID: [16]byte{1}, spanID: [8]byte{42}, } b := SpanContext{ traceID: [16]byte{1}, spanID: [8]byte{42}, } c := SpanContext{ traceID: [16]byte{2}, spanID: [8]byte{42}, } if a.Equal(b) != true { t.Error("Want: true, but have: false") } if a.Equal(c) != false { t.Error("Want: false, but have: true") } } func TestSpanContextIsSampled(t *testing.T) { for _, testcase := range []struct { name string tf TraceFlags want bool }{ { name: "SpanContext.IsSampled() returns false if sc is not sampled", want: false, }, { name: "SpanContext.IsSampled() returns true if sc is sampled", tf: FlagsSampled, want: true, }, } { t.Run(testcase.name, func(t *testing.T) { sc := SpanContext{ traceFlags: testcase.tf, } have := sc.IsSampled() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestSpanContextIsRemote(t *testing.T) { for _, testcase := range []struct { name string remote bool want bool }{ { name: "SpanContext.IsRemote() returns false if sc is not remote", want: false, }, { name: "SpanContext.IsRemote() returns true if sc is remote", remote: true, want: true, }, } { t.Run(testcase.name, func(t *testing.T) { sc := SpanContext{ remote: testcase.remote, } have := sc.IsRemote() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestSpanContextMarshalJSON(t *testing.T) { for _, testcase := range []struct { name string tid TraceID sid SpanID tstate TraceState tflags TraceFlags isRemote bool want []byte }{ { name: "SpanContext.MarshalJSON() returns json with partial data", tid: [16]byte{1}, sid: [8]byte{42}, want: []byte(`{"TraceID":"01000000000000000000000000000000","SpanID":"2a00000000000000","TraceFlags":"00","TraceState":"","Remote":false}`), }, { name: "SpanContext.MarshalJSON() returns json with full data", tid: [16]byte{1}, sid: [8]byte{42}, tflags: FlagsSampled, isRemote: true, tstate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, want: []byte(`{"TraceID":"01000000000000000000000000000000","SpanID":"2a00000000000000","TraceFlags":"01","TraceState":"foo=1","Remote":true}`), }, } { t.Run(testcase.name, func(t *testing.T) { sc := SpanContext{ traceID: testcase.tid, spanID: testcase.sid, traceFlags: testcase.tflags, traceState: testcase.tstate, remote: testcase.isRemote, } have, err := sc.MarshalJSON() if err != nil { t.Errorf("Marshaling failed: %v", err) } if !bytes.Equal(have, testcase.want) { t.Errorf("Want: %v, but have: %v", string(testcase.want), string(have)) } }) } } func TestSpanIDFromHex(t *testing.T) { for _, testcase := range []struct { name string hex string sid SpanID valid bool }{ { name: "Valid SpanID", sid: SpanID([8]byte{42}), hex: "2a00000000000000", valid: true, }, { name: "Invalid SpanID with invalid length", hex: "80f198ee56343ba", valid: false, }, { name: "Invalid SpanID with invalid char", hex: "80f198ee563433g7", valid: false, }, { name: "Invalid SpanID with uppercase", hex: "80f198ee53ba86F7", valid: false, }, { name: "Invalid SpanID with zero value", hex: "0000000000000000", valid: false, }, } { t.Run(testcase.name, func(t *testing.T) { sid, err := SpanIDFromHex(testcase.hex) if testcase.valid && err != nil { t.Errorf("Expected SpanID %s to be valid but end with error %s", testcase.hex, err.Error()) } else if !testcase.valid && err == nil { t.Errorf("Expected SpanID %s to be invalid but end no error", testcase.hex) } if sid != testcase.sid { t.Errorf("Want: %v, but have: %v", testcase.sid, sid) } }) } } func TestIsValidFromHex(t *testing.T) { for _, testcase := range []struct { name string hex string tid TraceID valid bool }{ { name: "Valid TraceID", tid: TraceID([16]byte{128, 241, 152, 238, 86, 52, 59, 168, 100, 254, 139, 42, 87, 211, 239, 247}), hex: "80f198ee56343ba864fe8b2a57d3eff7", valid: true, }, { name: "Invalid TraceID with invalid length", hex: "80f198ee56343ba864fe8b2a57d3eff", valid: false, }, { name: "Invalid TraceID with invalid char", hex: "80f198ee56343ba864fe8b2a57d3efg7", valid: false, }, { name: "Invalid TraceID with uppercase", hex: "80f198ee56343ba864fe8b2a57d3efF7", valid: false, }, { name: "Invalid TraceID with zero value", hex: "00000000000000000000000000000000", valid: false, }, } { t.Run(testcase.name, func(t *testing.T) { tid, err := TraceIDFromHex(testcase.hex) if testcase.valid && err != nil { t.Errorf("Expected TraceID %s to be valid but end with error %s", testcase.hex, err.Error()) } if !testcase.valid && err == nil { t.Errorf("Expected TraceID %s to be invalid but end no error", testcase.hex) } if tid != testcase.tid { t.Errorf("Want: %v, but have: %v", testcase.tid, tid) } }) } } func TestSpanContextHasTraceID(t *testing.T) { for _, testcase := range []struct { name string tid TraceID want bool }{ { name: "SpanContext.HasTraceID() returns true if both Low and High are nonzero", tid: TraceID([16]byte{1}), want: true, }, { name: "SpanContext.HasTraceID() returns false if neither Low nor High are nonzero", tid: TraceID{}, want: false, }, } { t.Run(testcase.name, func(t *testing.T) { //proto: func (sc SpanContext) HasTraceID() bool{} sc := SpanContext{traceID: testcase.tid} have := sc.HasTraceID() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestSpanContextHasSpanID(t *testing.T) { for _, testcase := range []struct { name string sc SpanContext want bool }{ { name: "SpanContext.HasSpanID() returns true if self.SpanID != 0", sc: SpanContext{spanID: [8]byte{42}}, want: true, }, { name: "SpanContext.HasSpanID() returns false if self.SpanID == 0", sc: SpanContext{}, want: false, }, } { t.Run(testcase.name, func(t *testing.T) { //proto: func (sc SpanContext) HasSpanID() bool {} have := testcase.sc.HasSpanID() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestTraceFlagsIsSampled(t *testing.T) { for _, testcase := range []struct { name string tf TraceFlags want bool }{ { name: "sampled", tf: FlagsSampled, want: true, }, { name: "unused bits are ignored, still not sampled", tf: ^FlagsSampled, want: false, }, { name: "unused bits are ignored, still sampled", tf: FlagsSampled | ^FlagsSampled, want: true, }, { name: "not sampled/default", want: false, }, } { t.Run(testcase.name, func(t *testing.T) { have := testcase.tf.IsSampled() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestTraceFlagsWithSampled(t *testing.T) { for _, testcase := range []struct { name string start TraceFlags sample bool want TraceFlags }{ { name: "sampled unchanged", start: FlagsSampled, want: FlagsSampled, sample: true, }, { name: "become sampled", want: FlagsSampled, sample: true, }, { name: "unused bits are ignored, still not sampled", start: ^FlagsSampled, want: ^FlagsSampled, sample: false, }, { name: "unused bits are ignored, becomes sampled", start: ^FlagsSampled, want: FlagsSampled | ^FlagsSampled, sample: true, }, { name: "not sampled/default", sample: false, }, } { t.Run(testcase.name, func(t *testing.T) { have := testcase.start.WithSampled(testcase.sample) if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) } }) } } func TestStringTraceID(t *testing.T) { for _, testcase := range []struct { name string tid TraceID want string }{ { name: "TraceID.String returns string representation of self.TraceID values > 0", tid: TraceID([16]byte{255}), want: "ff000000000000000000000000000000", }, { name: "TraceID.String returns string representation of self.TraceID values == 0", tid: TraceID([16]byte{}), want: "00000000000000000000000000000000", }, } { t.Run(testcase.name, func(t *testing.T) { //proto: func (t TraceID) String() string {} have := testcase.tid.String() if have != testcase.want { t.Errorf("Want: %s, but have: %s", testcase.want, have) } }) } } func TestStringSpanID(t *testing.T) { for _, testcase := range []struct { name string sid SpanID want string }{ { name: "SpanID.String returns string representation of self.SpanID values > 0", sid: SpanID([8]byte{255}), want: "ff00000000000000", }, { name: "SpanID.String returns string representation of self.SpanID values == 0", sid: SpanID([8]byte{}), want: "0000000000000000", }, } { t.Run(testcase.name, func(t *testing.T) { //proto: func (t TraceID) String() string {} have := testcase.sid.String() if have != testcase.want { t.Errorf("Want: %s, but have: %s", testcase.want, have) } }) } } func TestValidateSpanKind(t *testing.T) { tests := []struct { in SpanKind want SpanKind }{ { SpanKindUnspecified, SpanKindInternal, }, { SpanKindInternal, SpanKindInternal, }, { SpanKindServer, SpanKindServer, }, { SpanKindClient, SpanKindClient, }, { SpanKindProducer, SpanKindProducer, }, { SpanKindConsumer, SpanKindConsumer, }, } for _, test := range tests { if got := ValidateSpanKind(test.in); got != test.want { t.Errorf("ValidateSpanKind(%#v) = %#v, want %#v", test.in, got, test.want) } } } func TestSpanKindString(t *testing.T) { tests := []struct { in SpanKind want string }{ { SpanKindUnspecified, "unspecified", }, { SpanKindInternal, "internal", }, { SpanKindServer, "server", }, { SpanKindClient, "client", }, { SpanKindProducer, "producer", }, { SpanKindConsumer, "consumer", }, } for _, test := range tests { if got := test.in.String(); got != test.want { t.Errorf("%#v.String() = %#v, want %#v", test.in, got, test.want) } } } func assertSpanContextEqual(got SpanContext, want SpanContext) bool { return got.spanID == want.spanID && got.traceID == want.traceID && got.traceFlags == want.traceFlags && got.remote == want.remote && got.traceState.String() == want.traceState.String() } func TestNewSpanContext(t *testing.T) { testCases := []struct { name string config SpanContextConfig expectedSpanContext SpanContext }{ { name: "Complete SpanContext", config: SpanContextConfig{ TraceID: TraceID([16]byte{1}), SpanID: SpanID([8]byte{42}), TraceFlags: 0x1, TraceState: TraceState{list: []member{ {"foo", "bar"}, }}, }, expectedSpanContext: SpanContext{ traceID: TraceID([16]byte{1}), spanID: SpanID([8]byte{42}), traceFlags: 0x1, traceState: TraceState{list: []member{ {"foo", "bar"}, }}, }, }, { name: "Empty SpanContext", config: SpanContextConfig{}, expectedSpanContext: SpanContext{}, }, { name: "Partial SpanContext", config: SpanContextConfig{ TraceID: TraceID([16]byte{1}), SpanID: SpanID([8]byte{42}), }, expectedSpanContext: SpanContext{ traceID: TraceID([16]byte{1}), spanID: SpanID([8]byte{42}), traceFlags: 0x0, traceState: TraceState{}, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { sctx := NewSpanContext(tc.config) if !assertSpanContextEqual(sctx, tc.expectedSpanContext) { t.Fatalf("%s: Unexpected context created: %s", tc.name, cmp.Diff(sctx, tc.expectedSpanContext)) } }) } } func TestSpanContextDerivation(t *testing.T) { from := SpanContext{} to := SpanContext{traceID: TraceID([16]byte{1})} modified := from.WithTraceID(to.TraceID()) if !assertSpanContextEqual(modified, to) { t.Fatalf("WithTraceID: Unexpected context created: %s", cmp.Diff(modified, to)) } from = to to.spanID = SpanID([8]byte{42}) modified = from.WithSpanID(to.SpanID()) if !assertSpanContextEqual(modified, to) { t.Fatalf("WithSpanID: Unexpected context created: %s", cmp.Diff(modified, to)) } from = to to.traceFlags = 0x13 modified = from.WithTraceFlags(to.TraceFlags()) if !assertSpanContextEqual(modified, to) { t.Fatalf("WithTraceFlags: Unexpected context created: %s", cmp.Diff(modified, to)) } from = to to.traceState = TraceState{list: []member{{"foo", "bar"}}} modified = from.WithTraceState(to.TraceState()) if !assertSpanContextEqual(modified, to) { t.Fatalf("WithTraceState: Unexpected context created: %s", cmp.Diff(modified, to)) } } func TestLinkFromContext(t *testing.T) { k1v1 := attribute.String("key1", "value1") spanCtx := SpanContext{traceID: TraceID([16]byte{1}), remote: true} receiverCtx := ContextWithRemoteSpanContext(context.Background(), spanCtx) link := LinkFromContext(receiverCtx, k1v1) if !assertSpanContextEqual(link.SpanContext, spanCtx) { t.Fatalf("LinkFromContext: Unexpected context created: %s", cmp.Diff(link.SpanContext, spanCtx)) } assert.Equal(t, link.Attributes[0], k1v1) } golang-opentelemetry-otel-1.1.0/trace/tracestate.go000066400000000000000000000153711414226744000224120ustar00rootroot00000000000000// 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 trace // import "go.opentelemetry.io/otel/trace" import ( "encoding/json" "fmt" "regexp" "strings" ) var ( maxListMembers = 32 listDelimiter = "," // based on the W3C Trace Context specification, see // https://www.w3.org/TR/trace-context-1/#tracestate-header noTenantKeyFormat = `[a-z][_0-9a-z\-\*\/]{0,255}` withTenantKeyFormat = `[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}` valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]` keyRe = regexp.MustCompile(`^((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))$`) valueRe = regexp.MustCompile(`^(` + valueFormat + `)$`) memberRe = regexp.MustCompile(`^\s*((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))=(` + valueFormat + `)\s*$`) errInvalidKey errorConst = "invalid tracestate key" errInvalidValue errorConst = "invalid tracestate value" errInvalidMember errorConst = "invalid tracestate list-member" errMemberNumber errorConst = "too many list-members in tracestate" errDuplicate errorConst = "duplicate list-member in tracestate" ) type member struct { Key string Value string } func newMember(key, value string) (member, error) { if !keyRe.MatchString(key) { return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) } if !valueRe.MatchString(value) { return member{}, fmt.Errorf("%w: %s", errInvalidValue, value) } return member{Key: key, Value: value}, nil } func parseMemeber(m string) (member, error) { matches := memberRe.FindStringSubmatch(m) if len(matches) != 5 { return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) } return member{ Key: matches[1], Value: matches[4], }, nil } // String encodes member into a string compliant with the W3C Trace Context // specification. func (m member) String() string { return fmt.Sprintf("%s=%s", m.Key, m.Value) } // TraceState provides additional vendor-specific trace identification // information across different distributed tracing systems. It represents an // immutable list consisting of key/value pairs, each pair is referred to as a // list-member. // // TraceState conforms to the W3C Trace Context specification // (https://www.w3.org/TR/trace-context-1). All operations that create or copy // a TraceState do so by validating all input and will only produce TraceState // that conform to the specification. Specifically, this means that all // list-member's key/value pairs are valid, no duplicate list-members exist, // and the maximum number of list-members (32) is not exceeded. type TraceState struct { //nolint:revive // revive complains about stutter of `trace.TraceState` // list is the members in order. list []member } var _ json.Marshaler = TraceState{} // ParseTraceState attempts to decode a TraceState from the passed // string. It returns an error if the input is invalid according to the W3C // Trace Context specification. func ParseTraceState(tracestate string) (TraceState, error) { if tracestate == "" { return TraceState{}, nil } wrapErr := func(err error) error { return fmt.Errorf("failed to parse tracestate: %w", err) } var members []member found := make(map[string]struct{}) for _, memberStr := range strings.Split(tracestate, listDelimiter) { if len(memberStr) == 0 { continue } m, err := parseMemeber(memberStr) if err != nil { return TraceState{}, wrapErr(err) } if _, ok := found[m.Key]; ok { return TraceState{}, wrapErr(errDuplicate) } found[m.Key] = struct{}{} members = append(members, m) if n := len(members); n > maxListMembers { return TraceState{}, wrapErr(errMemberNumber) } } return TraceState{list: members}, nil } // MarshalJSON marshals the TraceState into JSON. func (ts TraceState) MarshalJSON() ([]byte, error) { return json.Marshal(ts.String()) } // String encodes the TraceState into a string compliant with the W3C // Trace Context specification. The returned string will be invalid if the // TraceState contains any invalid members. func (ts TraceState) String() string { members := make([]string, len(ts.list)) for i, m := range ts.list { members[i] = m.String() } return strings.Join(members, listDelimiter) } // Get returns the value paired with key from the corresponding TraceState // list-member if it exists, otherwise an empty string is returned. func (ts TraceState) Get(key string) string { for _, member := range ts.list { if member.Key == key { return member.Value } } return "" } // Insert adds a new list-member defined by the key/value pair to the // TraceState. If a list-member already exists for the given key, that // list-member's value is updated. The new or updated list-member is always // moved to the beginning of the TraceState as specified by the W3C Trace // Context specification. // // If key or value are invalid according to the W3C Trace Context // specification an error is returned with the original TraceState. // // If adding a new list-member means the TraceState would have more members // than is allowed an error is returned instead with the original TraceState. func (ts TraceState) Insert(key, value string) (TraceState, error) { m, err := newMember(key, value) if err != nil { return ts, err } cTS := ts.Delete(key) if cTS.Len()+1 > maxListMembers { // TODO (MrAlias): When the second version of the Trace Context // specification is published this needs to not return an error. // Instead it should drop the "right-most" member and insert the new // member at the front. // // https://github.com/w3c/trace-context/pull/448 return ts, fmt.Errorf("failed to insert: %w", errMemberNumber) } cTS.list = append(cTS.list, member{}) copy(cTS.list[1:], cTS.list) cTS.list[0] = m return cTS, nil } // Delete returns a copy of the TraceState with the list-member identified by // key removed. func (ts TraceState) Delete(key string) TraceState { members := make([]member, ts.Len()) copy(members, ts.list) for i, member := range ts.list { if member.Key == key { members = append(members[:i], members[i+1:]...) // TraceState should contain no duplicate members. break } } return TraceState{list: members} } // Len returns the number of list-members in the TraceState. func (ts TraceState) Len() int { return len(ts.list) } golang-opentelemetry-otel-1.1.0/trace/tracestate_test.go000066400000000000000000000337101414226744000234460ustar00rootroot00000000000000// 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 trace import ( "encoding/json" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Taken from the W3C tests: // https://github.com/w3c/trace-context/blob/dcd3ad9b7d6ac36f70ff3739874b73c11b0302a1/test/test_data.json var testcases = []struct { in string tracestate TraceState out string err error }{ { in: "foo=1,foo=1", err: errDuplicate, }, { in: "foo=1,foo=2", err: errDuplicate, }, { in: "foo =1", err: errInvalidMember, }, { in: "FOO=1", err: errInvalidMember, }, { in: "foo.bar=1", err: errInvalidMember, }, { in: "foo@=1,bar=2", err: errInvalidMember, }, { in: "@foo=1,bar=2", err: errInvalidMember, }, { in: "foo@@bar=1,bar=2", err: errInvalidMember, }, { in: "foo@bar@baz=1,bar=2", err: errInvalidMember, }, { in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", err: errInvalidMember, }, { in: "foo=1,tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@v=1", err: errInvalidMember, }, { in: "foo=1,t@vvvvvvvvvvvvvvv=1", err: errInvalidMember, }, { in: "foo=bar=baz", err: errInvalidMember, }, { in: "foo=,bar=3", err: errInvalidMember, }, { in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32,bar33=33", err: errMemberNumber, }, { in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", tracestate: TraceState{list: []member{ { Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/", Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", }, }}, }, { in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", tracestate: TraceState{list: []member{ { Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/", Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", }, }}, }, { // Empty input should result in no error and a zero value // TraceState being returned, that TraceState should be encoded as an // empty string. }, { in: "foo=1", out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { in: "foo=1,", out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { in: "foo=1,bar=2", out: "foo=1,bar=2", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, {Key: "bar", Value: "2"}, }}, }, { in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", out: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", tracestate: TraceState{list: []member{ { Key: "foo", Value: "1", }, { Key: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", Value: "1", }, }}, }, { in: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", out: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", tracestate: TraceState{list: []member{ { Key: "foo", Value: "1", }, { Key: "ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv", Value: "1", }, }}, }, { in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32", out: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32", tracestate: TraceState{list: []member{ {Key: "bar01", Value: "01"}, {Key: "bar02", Value: "02"}, {Key: "bar03", Value: "03"}, {Key: "bar04", Value: "04"}, {Key: "bar05", Value: "05"}, {Key: "bar06", Value: "06"}, {Key: "bar07", Value: "07"}, {Key: "bar08", Value: "08"}, {Key: "bar09", Value: "09"}, {Key: "bar10", Value: "10"}, {Key: "bar11", Value: "11"}, {Key: "bar12", Value: "12"}, {Key: "bar13", Value: "13"}, {Key: "bar14", Value: "14"}, {Key: "bar15", Value: "15"}, {Key: "bar16", Value: "16"}, {Key: "bar17", Value: "17"}, {Key: "bar18", Value: "18"}, {Key: "bar19", Value: "19"}, {Key: "bar20", Value: "20"}, {Key: "bar21", Value: "21"}, {Key: "bar22", Value: "22"}, {Key: "bar23", Value: "23"}, {Key: "bar24", Value: "24"}, {Key: "bar25", Value: "25"}, {Key: "bar26", Value: "26"}, {Key: "bar27", Value: "27"}, {Key: "bar28", Value: "28"}, {Key: "bar29", Value: "29"}, {Key: "bar30", Value: "30"}, {Key: "bar31", Value: "31"}, {Key: "bar32", Value: "32"}, }}, }, { in: "foo=1,bar=2,rojo=1,congo=2,baz=3", out: "foo=1,bar=2,rojo=1,congo=2,baz=3", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, {Key: "bar", Value: "2"}, {Key: "rojo", Value: "1"}, {Key: "congo", Value: "2"}, {Key: "baz", Value: "3"}, }}, }, { in: "foo=1 \t , \t bar=2, \t baz=3", out: "foo=1,bar=2,baz=3", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, {Key: "bar", Value: "2"}, {Key: "baz", Value: "3"}, }}, }, { in: "foo=1\t \t,\t \tbar=2,\t \tbaz=3", out: "foo=1,bar=2,baz=3", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, {Key: "bar", Value: "2"}, {Key: "baz", Value: "3"}, }}, }, { in: "foo=1 ", out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { in: "foo=1\t", out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { in: "foo=1 \t", out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, } var maxMembers = func() TraceState { members := make([]member, maxListMembers) for i := 0; i < maxListMembers; i++ { members[i] = member{ Key: fmt.Sprintf("key%d", i+1), Value: fmt.Sprintf("value%d", i+1), } } return TraceState{list: members} }() func TestParseTraceState(t *testing.T) { for _, tc := range testcases { got, err := ParseTraceState(tc.in) assert.Equal(t, tc.tracestate, got) if tc.err != nil { assert.ErrorIs(t, err, tc.err, tc.in) } else { assert.NoError(t, err, tc.in) } } } func TestTraceStateString(t *testing.T) { for _, tc := range testcases { if tc.err != nil { // Only test non-zero value TraceState. continue } assert.Equal(t, tc.out, tc.tracestate.String()) } } func TestTraceStateMarshalJSON(t *testing.T) { for _, tc := range testcases { if tc.err != nil { // Only test non-zero value TraceState. continue } // Encode UTF-8. expected, err := json.Marshal(tc.out) require.NoError(t, err) actual, err := json.Marshal(tc.tracestate) require.NoError(t, err) assert.Equal(t, expected, actual) } } func TestTraceStateGet(t *testing.T) { testCases := []struct { name string key string expected string }{ { name: "OK case", key: "key16", expected: "value16", }, { name: "not found", key: "keyxx", expected: "", }, { name: "invalid W3C key", key: "key!", expected: "", }, } for _, tc := range testCases { assert.Equal(t, tc.expected, maxMembers.Get(tc.key), tc.name) } } func TestTraceStateDelete(t *testing.T) { ts := TraceState{list: []member{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, {Key: "key3", Value: "val3"}, }} testCases := []struct { name string key string expected TraceState }{ { name: "OK case", key: "key2", expected: TraceState{list: []member{ {Key: "key1", Value: "val1"}, {Key: "key3", Value: "val3"}, }}, }, { name: "Non-existing key", key: "keyx", expected: TraceState{list: []member{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, {Key: "key3", Value: "val3"}, }}, }, { name: "Invalid key", key: "in va lid", expected: TraceState{list: []member{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, {Key: "key3", Value: "val3"}, }}, }, } for _, tc := range testCases { assert.Equal(t, tc.expected, ts.Delete(tc.key), tc.name) } } func TestTraceStateInsert(t *testing.T) { ts := TraceState{list: []member{ {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, {Key: "key3", Value: "val3"}, }} testCases := []struct { name string tracestate TraceState key, value string expected TraceState err error }{ { name: "add new", tracestate: ts, key: "key4@vendor", value: "val4", expected: TraceState{list: []member{ {Key: "key4@vendor", Value: "val4"}, {Key: "key1", Value: "val1"}, {Key: "key2", Value: "val2"}, {Key: "key3", Value: "val3"}, }}, }, { name: "replace", tracestate: ts, key: "key2", value: "valX", expected: TraceState{list: []member{ {Key: "key2", Value: "valX"}, {Key: "key1", Value: "val1"}, {Key: "key3", Value: "val3"}, }}, }, { name: "invalid key", tracestate: ts, key: "key!", value: "val", expected: ts, err: errInvalidKey, }, { name: "invalid value", tracestate: ts, key: "key", value: "v=l", expected: ts, err: errInvalidValue, }, { name: "invalid key/value", tracestate: ts, key: "key!", value: "v=l", expected: ts, err: errInvalidKey, }, { name: "too many entries", tracestate: maxMembers, key: "keyx", value: "valx", expected: maxMembers, err: errMemberNumber, }, } for _, tc := range testCases { actual, err := tc.tracestate.Insert(tc.key, tc.value) assert.ErrorIs(t, err, tc.err, tc.name) if tc.err != nil { assert.Equal(t, tc.tracestate, actual, tc.name) } else { assert.Equal(t, tc.expected, actual, tc.name) } } } func TestTraceStateLen(t *testing.T) { ts := TraceState{} assert.Equal(t, 0, ts.Len(), "zero value TraceState is empty") key := "key" ts = TraceState{list: []member{{key, "value"}}} assert.Equal(t, 1, ts.Len(), "TraceState with one value") } func TestTraceStateImmutable(t *testing.T) { k0, v0 := "k0", "v0" ts0 := TraceState{list: []member{{k0, v0}}} assert.Equal(t, v0, ts0.Get(k0)) // Insert should not modify the original. k1, v1 := "k1", "v1" ts1, err := ts0.Insert(k1, v1) require.NoError(t, err) assert.Equal(t, v0, ts0.Get(k0)) assert.Equal(t, "", ts0.Get(k1)) assert.Equal(t, v0, ts1.Get(k0)) assert.Equal(t, v1, ts1.Get(k1)) // Update should not modify the original. v2 := "v2" ts2, err := ts1.Insert(k1, v2) require.NoError(t, err) assert.Equal(t, v0, ts0.Get(k0)) assert.Equal(t, "", ts0.Get(k1)) assert.Equal(t, v0, ts1.Get(k0)) assert.Equal(t, v1, ts1.Get(k1)) assert.Equal(t, v0, ts2.Get(k0)) assert.Equal(t, v2, ts2.Get(k1)) // Delete should not modify the original. ts3 := ts2.Delete(k0) assert.Equal(t, v0, ts0.Get(k0)) assert.Equal(t, v0, ts1.Get(k0)) assert.Equal(t, v0, ts2.Get(k0)) assert.Equal(t, "", ts3.Get(k0)) } golang-opentelemetry-otel-1.1.0/trace_test.go000066400000000000000000000022601414226744000213030ustar00rootroot00000000000000// 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 otel import ( "testing" "go.opentelemetry.io/otel/internal/trace/noop" "go.opentelemetry.io/otel/trace" ) type testTracerProvider struct{} var _ trace.TracerProvider = &testTracerProvider{} func (*testTracerProvider) Tracer(_ string, _ ...trace.TracerOption) trace.Tracer { return noop.Tracer } func TestMultipleGlobalTracerProvider(t *testing.T) { p1 := testTracerProvider{} p2 := trace.NewNoopTracerProvider() SetTracerProvider(&p1) SetTracerProvider(p2) got := GetTracerProvider() want := p2 if got != want { t.Fatalf("TracerProvider: got %p, want %p\n", got, want) } } golang-opentelemetry-otel-1.1.0/verify_examples.sh000077500000000000000000000045151414226744000223650ustar00rootroot00000000000000#!/bin/bash # 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. set -euo pipefail cd $(dirname $0) TOOLS_DIR=$(pwd)/.tools if [ -z "${GOPATH}" ] ; then printf "GOPATH is not defined.\n" exit -1 fi if [ ! -d "${GOPATH}" ] ; then printf "GOPATH ${GOPATH} is invalid \n" exit -1 fi # Pre-requisites if ! git diff --quiet; then \ git status printf "\n\nError: working tree is not clean\n" exit -1 fi if [ "$(git tag --contains $(git log -1 --pretty=format:"%H"))" = "" ] ; then printf "$(git log -1)" printf "\n\nError: HEAD is not pointing to a tagged version" fi make ${TOOLS_DIR}/gojq DIR_TMP="${GOPATH}/src/oteltmp/" rm -rf $DIR_TMP mkdir -p $DIR_TMP printf "Copy examples to ${DIR_TMP}\n" cp -a ./example ${DIR_TMP} # Update go.mod files printf "Update go.mod: rename module and remove replace\n" PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep 'example' | sed 's/^\.\///' | sort) for dir in $PACKAGE_DIRS; do printf " Update go.mod for $dir\n" (cd "${DIR_TMP}/${dir}" && \ # replaces is ("mod1" "mod2" …) replaces=($(go mod edit -json | ${TOOLS_DIR}/gojq '.Replace[].Old.Path')) && \ # strip double quotes replaces=("${replaces[@]%\"}") && \ replaces=("${replaces[@]#\"}") && \ # make an array (-dropreplace=mod1 -dropreplace=mod2 …) dropreplaces=("${replaces[@]/#/-dropreplace=}") && \ go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \ go mod tidy) done printf "Update done:\n\n" # Build directories that contain main package. These directories are different than # directories that contain go.mod files. printf "Build examples:\n" EXAMPLES=$(./get_main_pkgs.sh ./example) for ex in $EXAMPLES; do printf " Build $ex in ${DIR_TMP}/${ex}\n" (cd "${DIR_TMP}/${ex}" && \ go build .) done # Cleanup printf "Remove copied files.\n" rm -rf $DIR_TMP golang-opentelemetry-otel-1.1.0/version.go000066400000000000000000000013661414226744000206410ustar00rootroot00000000000000// 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 otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { return "1.1.0" } golang-opentelemetry-otel-1.1.0/version_test.go000066400000000000000000000021201414226744000216650ustar00rootroot00000000000000// 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 otel_test import ( "regexp" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel" ) // regex taken from https://github.com/Masterminds/semver/tree/v3.1.1 var versionRegex = regexp.MustCompile(`^v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$`) func TestVersionSemver(t *testing.T) { v := otel.Version() assert.NotNil(t, versionRegex.FindStringSubmatch(v), "version is not semver: %s", v) } golang-opentelemetry-otel-1.1.0/versions.yaml000066400000000000000000000046231414226744000213600ustar00rootroot00000000000000# 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. module-sets: stable-v1: version: v1.1.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opentracing - go.opentelemetry.io/otel/example/fib - go.opentelemetry.io/otel/example/jaeger - go.opentelemetry.io/otel/example/namedtracer - go.opentelemetry.io/otel/example/otel-collector - go.opentelemetry.io/otel/example/passthrough - go.opentelemetry.io/otel/example/zipkin - go.opentelemetry.io/otel/exporters/jaeger - go.opentelemetry.io/otel/exporters/zipkin - go.opentelemetry.io/otel/exporters/otlp/otlptrace - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp - go.opentelemetry.io/otel/exporters/stdout/stdouttrace - go.opentelemetry.io/otel/trace - go.opentelemetry.io/otel/sdk experimental-metrics: version: v0.24.0 modules: - go.opentelemetry.io/otel/example/prometheus - go.opentelemetry.io/otel/exporters/otlp/otlpmetric - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp - go.opentelemetry.io/otel/exporters/prometheus - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric - go.opentelemetry.io/otel/internal/metric - go.opentelemetry.io/otel/metric - go.opentelemetry.io/otel/sdk/export/metric - go.opentelemetry.io/otel/sdk/metric experimental-schema: version: v0.0.1 modules: - go.opentelemetry.io/otel/schema bridge: version: v0.24.0 modules: - go.opentelemetry.io/otel/bridge/opencensus - go.opentelemetry.io/otel/bridge/opencensus/test - go.opentelemetry.io/otel/example/opencensus excluded-modules: - go.opentelemetry.io/otel/internal/tools golang-opentelemetry-otel-1.1.0/website_docs/000077500000000000000000000000001414226744000212715ustar00rootroot00000000000000golang-opentelemetry-otel-1.1.0/website_docs/_index.md000066400000000000000000000025511414226744000230640ustar00rootroot00000000000000--- title: Go description: >- A language-specific implementation of OpenTelemetry in Go. aliases: [/golang, /golang/metrics, /golang/tracing] cascade: github_repo: &repo https://github.com/open-telemetry/opentelemetry-go github_subdir: website_docs path_base_for_github_subdir: content/en/docs/go/ github_project_repo: *repo weight: 16 --- This is the OpenTelemetry for Go documentation. OpenTelemetry is an observability framework -- an API, SDK, and tools that are designed to aid in the generation and collection of application telemetry data such as metrics, logs, and traces. This documentation is designed to help you understand how to get started using OpenTelemetry for Go. ## Status and Releases The current status of the major functional components for OpenTelemetry Go is as follows: | Tracing | Metrics | Logging | | ------- | ------- | ------- | | Stable | Alpha | Not Yet Implemented | The current release can be found [here](https://github.com/open-telemetry/opentelemetry-go/releases) ## Further Reading - [godoc](https://pkg.go.dev/go.opentelemetry.io/otel) - [Examples](https://github.com/open-telemetry/opentelemetry-go/tree/main/example) - [Contrib Repository](https://github.com/open-telemetry/opentelemetry-go-contrib) golang-opentelemetry-otel-1.1.0/website_docs/exporting_data.md000066400000000000000000000077301414226744000246320ustar00rootroot00000000000000--- title: "Processing and Exporting Data" weight: 4 --- Once you've instrumented your code, you need to get the data out in order to do anything useful with it. This page will cover the basics of the process and export pipeline. # Sampling Sampling is a process that restricts the amount of traces that are generated by a system. The exact sampler you should use depends on your specific needs, but in general you should make a decision at the start of a trace, and allow the sampling decision to propagate to other services. A sampler needs to be set on the tracer provider when its configured, as follows: ```go provider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), ) ``` `AlwaysSample` and `NeverSample` are fairly self-explanatory. Always means that every trace will be sampled, the converse holds as true for Never. When you're getting started, or in a development environment, you'll almost always want to use `AlwaysSample`. Other samplers include: * `TraceIDRatioBased`, which will sample a fraction of traces, based on the fraction given to the sampler. Thus, if you set this to .5, half of traces will be sampled. * `ParentBased`, which behaves differently based on the incoming sampling decision. In general, this will sample spans that have parents that were sampled, and will not sample spans whose parents were _not_ sampled. When you're in production, you should consider using the `TraceIDRatioBased` sampler with the `ParentBased` sampler. # Resources Resources are a special type of attribute that apply to all spans generated by a process. These should be used to represent underlying metadata about a process that's non-ephemeral - for example, the hostname of a process, or its instance ID. Resources should be assigned to a tracer provider at its initialization, and are created much like attributes: ```go resources := resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("myService"), semconv.ServiceVersionKey.String("1.0.0"), semconv.ServiceInstanceIDKey.String("abcdef12345"), ) provider := sdktrace.NewTracerProvider( ... sdktrace.WithResource(resources), ) ``` Note the use of the `semconv` package to provide conventional names for resource attributes. This helps ensure that consumers of telemetry produced with these semantic conventions can easily discover relevant attributes and understand their meaning. Resources can also be detected automatically through `resource.Detector` implementations. These `Detector`s may discover information about the currently running process, the operating system it is running on, the cloud provider hosting that operating system instance, or any number of other resource attributes. ```go resources := resource.New(context.Background(), // Builtin detectors provide default values and support // OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables resource.WithBuiltinDetectors(), resource.WithProcess(), // This option configures a set of Detectors that discover process information resource.WithDetectors(thirdparty.Detector{}), // Bring your own external Detector implementation resource.WithAttributes(attribute.String("foo", "bar")), // Or specify resource attributes directly ) ``` # OTLP Exporter OpenTelemetry Protocol (OTLP) export is available in the `go.opentelemetry.io/otel/exporters/otlp/otlptrace` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetrics` packages. Please find more documentation on [GitHub](https://github.com/open-telemetry/opentelemetry-go/tree/main/exporters/otlp) # Jaeger Exporter Jaeger export is available in the `go.opentelemetry.io/otel/exporters/jaeger` package. Please find more documentation on [GitHub](https://github.com/open-telemetry/opentelemetry-go/tree/main/exporters/jaeger) # Prometheus Exporter Prometheus export is available in the `go.opentelemetry.io/otel/exporters/prometheus` package. Please find more documentation on [GitHub](https://github.com/open-telemetry/opentelemetry-go/tree/main/exporters/prometheus) golang-opentelemetry-otel-1.1.0/website_docs/getting-started.md000066400000000000000000000466241414226744000247340ustar00rootroot00000000000000--- title: "Getting Started" weight: 2 --- Welcome to the OpenTelemetry for Go getting started guide! This guide will walk you through the basic steps in installing, instrumenting with, configuring, and exporting data from OpenTelemetry. Before you get started, be sure to have Go 1.15 or newer installed. Understand how a system is functioning when it is failing or having issues is critical to resolving those issues. One strategy to understand this is with tracing. This guide shows how the OpenTelemetry Go project can be used to trace an example application. You will start with an application that computes Fibonacci numbers for users, and from there you will add instrumentation to produce tracing telemetry with OpenTelemetry Go. To start building the application, make a new directory named `fib` to house our Fibonacci project. Next, add the following to a new file named `fib.go` in that directory. ```go package main // Fibonacci returns the n-th fibonacci number. func Fibonacci(n uint) (uint64, error) { if n <= 1 { return uint64(n), nil } var n2, n1 uint64 = 0, 1 for i := uint(2); i < n; i++ { n2, n1 = n1, n1+n2 } return n2 + n1, nil } ``` With your core logic added, you can now build your application around it. Add a new `app.go` file with the following application logic. ```go package main import ( "context" "fmt" "io" "log" ) // App is an Fibonacci computation application. type App struct { r io.Reader l *log.Logger } // NewApp returns a new App. func NewApp(r io.Reader, l *log.Logger) *App { return &App{r: r, l: l} } // Run starts polling users for Fibonacci number requests and writes results. func (a *App) Run(ctx context.Context) error { for { n, err := a.Poll(ctx) if err != nil { return err } a.Write(ctx, n) } } // Poll asks a user for input and returns the request. func (a *App) Poll(ctx context.Context) (uint, error) { a.l.Print("What Fibonacci number would you like to know: ") var n uint _, err := fmt.Fscanf(a.r, "%d\n", &n) return n, err } // Write writes the n-th Fibonacci number back to the user. func (a *App) Write(ctx context.Context, n uint) { f, err := Fibonacci(n) if err != nil { a.l.Printf("Fibonacci(%d): %v\n", n, err) } else { a.l.Printf("Fibonacci(%d) = %d\n", n, f) } } ``` With your application fully composed, you need a `main()` function to actually run the application. In a new `main.go` file add the following run logic. ```go package main import ( "context" "log" "os" ) func main() { l := log.New(os.Stdout, "", 0) sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) errCh := make(chan error) app := NewApp(os.Stdin, l) go func() { errCh <- app.Run(context.Background()) }() select { case <-sigCh: l.Println("\ngoodbye") return case err := <-errCh: if err != nil { l.Fatal(err) } } } ``` With the code complete it is almost time to run the application. Before you can do that you need to initialize this directory as a Go module. From your terminal, run the command `go mod init fib` in the `fib` directory. This will create a `go.mod` file, which is used by Go to manage imports. Now you should be able to run the application! ```sh $ go run . What Fibonacci number would you like to know: 42 Fibonacci(42) = 267914296 What Fibonacci number would you like to know: ^C goodbye ``` The application can be exited with CTRL+C. You should see a similar output as above, if not make sure to go back and fix any errors. # Trace Instrumentation OpenTelemetry is split into two parts: an API to instrument code with, and SDKs that implement the API. To start integrating OpenTelemetry into any project, the API is used to define how telemetry is generated. To generate tracing telemetry in your application you will use the OpenTelemetry Trace API from the `go.opentelemetry.io/otel/trace` package. First, you need to install the necessary packages for the Trace API. Run the following command in your working directory. ```sh go get go.opentelemetry.io/otel \ go.opentelemetry.io/otel/trace ``` Now that the packages installed you can start updating your application with imports you will use in the `app.go` file. ```go import ( "context" "fmt" "io" "log" "strconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) ``` With the imports added, you can start instrumenting. The OpenTelemetry Tracing API provides a `Tracer` to create traces. These `Tracer`s are designed to be associated with one instrumentation library. That way telemetry they produce can be understood to come from that part of a code base. To uniquely identify your application to the `Tracer` you will use create a constant with the package name in `app.go`. ```go // name is the Tracer name used to identify this instrumentation library. const name = "fib" ``` Using the full-qualified package name, something that should be unique for Go packages, is the standard way to identify a `Tracer`. If your example package name differs, be sure to update the name you use here to match. Everything should be in place now to start tracing your application. But first, what is a trace? And, how exactly should you build them for you application? To back up a bit, a trace is a type of telemetry that represents work being done by a service. A trace is a record of the connection(s) between participants processing a transaction, often through client/server requests processing and other forms of communication. Each part of the work that a service performs is represented in the trace by a span. Those spans are not just an unordered collection. Like the call stack of our application, those spans are defined with relationships to one another. The "root" span is the only span without a parent, it represents how a service request is started. All other spans have a parent relationship to another span in the same trace. If this last part about span relationships doesn't make to much sense now, don't worry. The important thing to take away is each part of your code that does work should to be represented as a span. You will have a better understanding of these span relationships after you instrument your code, so let's get started. Start by instrumenting the `Run` method. ```go // Run starts polling users for Fibonacci number requests and writes results. func (a *App) Run(ctx context.Context) error { for { var span trace.Span ctx, span = otel.Tracer(name).Start(ctx, "Run") n, err := a.Poll(ctx) if err != nil { span.End() return err } a.Write(ctx, n) span.End() } } ``` The above code creates a span for every iteration of the for loop. The span is created using a `Tracer` from the global `TracerProvider`. You will learn more about `TracerProvider`s and handle the other side of setting up a global `TracerProvider` when you install an SDK in a later section. For now, as an instrumentation author, all you need to worry about is that you are using an appropriately named `Tracer` from a `TracerProvider` when you write `otel.Tracer(name)`. Next, instrument the `Poll` method. ```go // Poll asks a user for input and returns the request. func (a *App) Poll(ctx context.Context) (uint, error) { _, span := otel.Tracer(name).Start(ctx, "Poll") defer span.End() a.l.Print("What Fibonacci number would you like to know: ") var n uint _, err := fmt.Fscanf(a.r, "%d", &n) // Store n as a string to not overflow an int64. nStr := strconv.FormatUint(uint64(n), 10) span.SetAttributes(attribute.String("request.n", nStr)) return n, err } ``` Similar to the `Run` method instrumentation, this adds a span to the method to track the computation performed. However, it also adds an attribute to annotate the span. This annotation is something you can add when you think a user of your application will want to see the state or details about the run environment when looking at telemetry. Finally, instrument the `Write` method. ```go // Write writes the n-th Fibonacci number back to the user. func (a *App) Write(ctx context.Context, n uint) { var span trace.Span ctx, span = otel.Tracer(name).Start(ctx, "Write") defer span.End() f, err := func(ctx context.Context) (uint64, error) { _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() return Fibonacci(n) }(ctx) if err != nil { a.l.Printf("Fibonacci(%d): %v\n", n, err) } else { a.l.Printf("Fibonacci(%d) = %d\n", n, f) } } ``` This method is instrumented with two spans. One to track the `Write` method itself, and another to track the call to the core logic with the `Fibonacci` function. Do you see how context is passed through the spans? Do you see how this also defines the relationship between spans? In OpenTelemetry Go the span relationships are defined explicitly with a `context.Context`. When a span is created a context is returned alongside the span. That context will contain a reference to the created span. If that context is used when creating another span the two spans will be related. The original span will become the new span's parent, and as a corollary, the new span is said to be a child of the original. This hierarchy gives traces structure, structure that helps show a computation path through a system. Based on what you instrumented above and this understanding of span relationships you should expect a trace for each execution of the run loop to look like this. ``` Run ├── Poll └── Write └── Fibonacci ``` A `Run` span will be a parent to both a `Poll` and `Write` span, and the `Write` span will be a parent to a `Fibonacci` span. Now how do you actually see the produced spans? To do this you will need to configure and install an SDK. # SDK Installation OpenTelemetry is designed to be modular in its implementation of the OpenTelemetry API. The OpenTelemetry Go project offers an SDK package, `go.opentelemetry.io/otel/sdk`, that implements this API and adheres to the OpenTelemetry specification. To start using this SDK you will first need to create an exporter, but before anything can happen we need to install some packages. Run the following in the `fib` directory to install the trace STDOUT exporter and the SDK. ```sh $ go get go.opentelemetry.io/otel/sdk \ go.opentelemetry.io/otel/exporters/stdout/stdouttrace ``` Now add the needed imports to `main.go`. ```go import ( "context" "io" "log" "os" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/trace" ) ``` ## Creating a Console Exporter The SDK connects telemetry from the OpenTelemetry API to exporters. Exporters are packages that allow telemetry data to be emitted somewhere - either to the console (which is what we're doing here), or to a remote system or collector for further analysis and/or enrichment. OpenTelemetry supports a variety of exporters through its ecosystem including popular open source tools like Jaeger, Zipkin, and Prometheus. To initialize the console exporter, add the following function to the `main.go` file: ```go // newExporter returns a console exporter. func newExporter(w io.Writer) (trace.SpanExporter, error) { return stdouttrace.New( stdouttrace.WithWriter(w), // Use human readable output. stdouttrace.WithPrettyPrint(), // Do not print timestamps for the demo. stdouttrace.WithoutTimestamps(), ) } ``` This creates a new console exporter with basic options. You will use this function later when you configure the SDK to send telemetry data to it, but first you need to make sure that data is identifiable. ## Creating a Resource Telemetry data can be crucial to solving issues with a service. The catch is, you need a way to identify what service, or even what service instance, that data is coming from. OpenTelemetry uses a `Resource` to represent the entity producing telemetry. Add the following function to the `main.go` file to create an appropriate `Resource` for the application. ```go // newResource returns a resource describing this application. func newResource() *resource.Resource { r, _ := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("fib"), semconv.ServiceVersionKey.String("v0.1.0"), attribute.String("environment", "demo"), ), ) return r } ``` Any information you would like to associate with all telemetry data the SDK handles can be added to the returned `Resource`. This is done by registering the `Resource` with the `TracerProvider`. Something you can now create! ## Installing a Tracer Provider You have your application instrumented to produce telemetry data and you have an exporter to send that data to the console, but how are they connected? This is where the `TracerProvider` is used. It is a centralized point where instrumentation will get a `Tracer` from and funnels the telemetry data from these `Tracer`s to export pipelines. The pipelines that receive and ultimately transmit data to exporters are called `SpanProcessor`s. A `TracerProvider` can be configured to have multiple span processors, but for this example you will only need to configure only one. Update your `main` function in `main.go` with the following. ```go func main() { l := log.New(os.Stdout, "", 0) // Write telemetry data to a file. f, err := os.Create("traces.txt") if err != nil { l.Fatal(err) } defer f.Close() exp, err := newExporter(f) if err != nil { l.Fatal(err) } tp := trace.NewTracerProvider( trace.WithBatcher(exp), trace.WithResource(newResource()), ) defer func() { if err := tp.Shutdown(context.Background()); err != nil { l.Fatal(err) } }() otel.SetTracerProvider(tp) /* … */ } ``` There's a fair amount going on here. First you are creating a console exporter that will export to a file. You are then registering the exporter with a new `TracerProvider`. This is done with a `BatchSpanProcessor` when it is passed to the `trace.WithBatcher` option. Batching data is a good practice and will help not overload systems downstream. Finally, with the `TracerProvider` created, you are deferring a function to flush and stop it, and registering it as the global OpenTelemetry `TracerProvider`. Do you remember in the previous instrumentation section when we used the global `TracerProvider` to get a `Tracer`? This last step, registering the `TracerProvider` globally, is what will connect that instrumentation's `Tracer` with this `TracerProvider`. This pattern, using a global `TracerProvider`, is convenient, but not always appropriate. `TracerProvider`s can be explicitly passed to instrumentation or inferred from a context that contains a span. For this simple example using a global provider makes sense, but for more complex or distributed codebases these other ways of passing `TracerProvider`s may make more sense. # Putting It All Together You should now have a working application that produces trace telemetry data! Give it a try. ```sh $ go run . What Fibonacci number would you like to know: 42 Fibonacci(42) = 267914296 What Fibonacci number would you like to know: ^C goodbye ``` A new file named `traces.txt` should be created in your working directory. All the traces created from running your application should be in there! # (Bonus) Errors At this point you have a working application and it is producing tracing telemetry data. Unfortunately, it was discovered that there is an error in the core functionality of the `fib` module. ```sh $ go run . What Fibonacci number would you like to know: 100 Fibonacci(100) = 3736710778780434371 # … ``` But the 100-th Fibonacci number is `354224848179261915075`, not `3736710778780434371`! This application is only meant as a demo, but it shouldn't return wrong values. Update the `Fibonacci` function to return an error instead of computing incorrect values. ```go // Fibonacci returns the n-th fibonacci number. An error is returned if the // fibonacci number cannot be represented as a uint64. func Fibonacci(n uint) (uint64, error) { if n <= 1 { return uint64(n), nil } if n > 93 { return 0, fmt.Errorf("unsupported fibonacci number %d: too large", n) } var n2, n1 uint64 = 0, 1 for i := uint(2); i < n; i++ { n2, n1 = n1, n1+n2 } return n2 + n1, nil } ``` Great, you have fixed the code, but it would be ideal to include errors returned to a user in the telemetry data. Luckily, spans can be configured to communicate this information. Update the `Write` method in `app.go` with the following code. ```go // Write writes the n-th Fibonacci number back to the user. func (a *App) Write(ctx context.Context, n uint) { var span trace.Span ctx, span = otel.Tracer(name).Start(ctx, "Write") defer span.End() f, err := func(ctx context.Context) (uint64, error) { _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() f, err := Fibonacci(n) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) } return f, err }(ctx) /* … */ } ``` With this change any error returned from the `Fibonacci` function will mark that span as an error and record an event describing the error. This is a great start, but it is not the only error returned in from the application. If a user makes a request for a non unsigned integer value the application will fail. Update the `Poll` method with a similar fix to capture this error in the telemetry data. ```go // Poll asks a user for input and returns the request. func (a *App) Poll(ctx context.Context) (uint, error) { _, span := otel.Tracer(name).Start(ctx, "Poll") defer span.End() a.l.Print("What Fibonacci number would you like to know: ") var n uint _, err := fmt.Fscanf(a.r, "%d", &n) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) return 0, err } // Store n as a string to not overflow an int64. nStr := strconv.FormatUint(uint64(n), 10) span.SetAttributes(attribute.String("request.n", nStr)) return n, nil } ``` All that is left is updating imports for the `app.go` file to include the `go.opentelemetry.io/otel/codes` package. ```go import ( "context" "fmt" "io" "log" "strconv" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) ``` With these fixes in place and the instrumentation updated, re-trigger the bug. ```sh $ go run . What Fibonacci number would you like to know: 100 Fibonacci(100): unsupported fibonacci number 100: too large What Fibonacci number would you like to know: ^C goodbye ``` Excellent! The application no longer returns wrong values, and looking at the telemetry data in the `traces.txt` file you should see the error captured as an event. ``` "Events": [ { "Name": "exception", "Attributes": [ { "Key": "exception.type", "Value": { "Type": "STRING", "Value": "*errors.errorString" } }, { "Key": "exception.message", "Value": { "Type": "STRING", "Value": "unsupported fibonacci number 100: too large" } } ], ... } ] ``` # What's Next This guide has walked you through adding tracing instrumentation to an application and using a console exporter to send telemetry data to a file. There are many other topics to cover in OpenTelemetry, but you should be ready to start adding OpenTelemetry Go to your projects at this point. Go instrument your code! For more information about instrumenting your code and things you can do with spans, refer to the [Instrumenting](https://opentelemetry.io/docs/go/instrumentation/) documentation. Likewise, advanced topics about processing and exporting telemetry data can be found in the [Processing and Exporting Data](https://opentelemetry.io/docs/go/exporting_data/) documentation. golang-opentelemetry-otel-1.1.0/website_docs/instrumentation.md000066400000000000000000000114141414226744000250570ustar00rootroot00000000000000--- title: "Instrumentation" weight: 3 --- Instrumentation is the process of adding observability code to your application. There are two general types of instrumentation - automatic, and manual - and you should be familiar with both in order to effectively instrument your software. # Creating Spans Spans are created by tracers, which can be acquired from a Tracer Provider. ```go ctx := context.Background() tracer := otel.Tracer("example/main") var span trace.Span ctx, span = tracer.Start(ctx, "helloWorld") defer span.End() ``` In Go, the `context` package is used to store the active span. When you start a span, you'll get a handle on not only the span that's created, but the modified context that contains it. When starting a new span using this context, a parent-child relationship will automatically be created between the two spans, as seen here: ```go func parentFunction() { ctx := context.Background() var parentSpan trace.Span ctx, parentSpan = tracer.Start(ctx, "parent") defer parentSpan.End() // call our child function childFunction(ctx) // do more work, when this function ends, parentSpan will complete. } func childFunction(ctx context.Context) { var childSpan trace.Span ctx, childSpan = tracer.Start(ctx, "child") defer childSpan.End() // do work here, when this function returns, childSpan will complete. } ``` Once a span has completed, it is immutable and can no longer be modified. ## Attributes Attributes are keys and values that are applied as metadata to your spans and are useful for aggregating, filtering, and grouping traces. Attributes can be added at span creation, or at any other time during the lifecycle of a span before it has completed. ```go // setting attributes at creation... ctx, span = tracer.Start(ctx, "attributesAtCreation", trace.WithAttributes(attribute.String("hello", "world"))) // ... and after creation span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!")) ``` Attribute keys can be precomputed, as well - ```go var myKey = attribute.Key("myCoolAttribute") span.SetAttributes(myKey.String("a value")) ``` ### Semantic Attributes Semantic Attributes are attributes that are defined by the OpenTelemetry Specification in order to provide a shared set of attribute keys across multiple languages, frameworks, and runtimes for common concepts like HTTP methods, status codes, user agents, and more. These attributes are available in the `go.opentelemetry.io/otel/semconv/v1.7.0` package. Tracing semantic conventions can be found [in this document](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions) ## Events An event is a human-readable message on a span that represents "something happening" during it's lifetime. For example, imagine a function that requires exclusive access to a resource that is under a mutex. An event could be created at two points - once, when we try to gain access to the resource, and another when we acquire the mutex. ```go span.AddEvent("Acquiring lock") mutex.Lock() span.AddEvent("Got lock, doing work...") // do stuff span.AddEvent("Unlocking") mutex.Unlock() ``` A useful characteristic of events is that their timestamps are displayed as offsets from the beginning of the span, allowing you to easily see how much time elapsed between them. Events can also have attributes of their own - ```go span.AddEvent("Cancelled wait due to external signal", trace.WithAttributes(attribute.Int("pid", 4328), attribute.String("signal", "SIGHUP"))) ``` # Creating Metrics The metrics API is currently unstable, documentation TBA. # Propagators and Context Traces can extend beyond a single process. This requires _context propagation_, a mechanism where identifiers for a trace are sent to remote processes. In order to propagate trace context over the wire, a propagator must be registered with the OpenTelemetry API. ```go import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" ) ... otel.SetTextMapPropagator(propagation.TraceContext{}) ``` > OpenTelemetry also supports the B3 header format, for compatibility with existing tracing systems (`go.opentelemetry.io/contrib/propagators/b3`) that do not support the W3C TraceContext standard. After configuring context propagation, you'll most likely want to use automatic instrumentation to handle the behind-the-scenes work of actually managing serializing the context. # Automatic Instrumentation Automatic instrumentation, broadly, refers to instrumentation code that you didn't write. OpenTelemetry for Go supports this process through wrappers and helper functions around many popular frameworks and libraries. You can find a current list [here](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation), as well as at the [registry](/registry).