diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d326b38e49c..5e4fc315deb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,8 @@ -# 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 +# File generated by "make dependabot-generate"; DO NOT EDIT. version: 2 updates: - - - package-ecosystem: github-actions + - package-ecosystem: github-actions directory: / labels: - dependencies @@ -16,8 +11,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: / labels: - dependencies @@ -26,18 +20,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /bridge/opentracing - labels: - - dependencies - - go - - "Skip Changelog" - schedule: - day: sunday - interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /bridge/opencensus labels: - dependencies @@ -46,8 +29,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /bridge/opencensus/test labels: - dependencies @@ -56,9 +38,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /example/fib + - package-ecosystem: gomod + directory: /bridge/opentracing labels: - dependencies - go @@ -66,9 +47,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /example/prom-collector + - package-ecosystem: gomod + directory: /example/fib labels: - dependencies - go @@ -76,8 +56,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/jaeger labels: - dependencies @@ -86,8 +65,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/namedtracer labels: - dependencies @@ -96,8 +74,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/opencensus labels: - dependencies @@ -106,8 +83,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/otel-collector labels: - dependencies @@ -116,8 +92,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/passthrough labels: - dependencies @@ -126,8 +101,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/prometheus labels: - dependencies @@ -136,8 +110,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /example/zipkin labels: - dependencies @@ -146,9 +119,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/prometheus + - package-ecosystem: gomod + directory: /exporters/jaeger labels: - dependencies - go @@ -156,9 +128,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/stdout/stdouttrace + - package-ecosystem: gomod + directory: /exporters/otlp/internal/retry labels: - dependencies - go @@ -166,9 +137,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/stdout/stdoutmetric + - package-ecosystem: gomod + directory: /exporters/otlp/otlpmetric labels: - dependencies - go @@ -176,9 +146,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/jaeger + - package-ecosystem: gomod + directory: /exporters/otlp/otlpmetric/otlpmetricgrpc labels: - dependencies - go @@ -186,9 +155,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/zipkin + - package-ecosystem: gomod + directory: /exporters/otlp/otlpmetric/otlpmetrichttp labels: - dependencies - go @@ -196,9 +164,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /sdk + - package-ecosystem: gomod + directory: /exporters/otlp/otlptrace labels: - dependencies - go @@ -206,9 +173,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /internal/metric + - package-ecosystem: gomod + directory: /exporters/otlp/otlptrace/otlptracegrpc labels: - dependencies - go @@ -216,9 +182,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /internal/tools + - package-ecosystem: gomod + directory: /exporters/otlp/otlptrace/otlptracehttp labels: - dependencies - go @@ -226,9 +191,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /metric + - package-ecosystem: gomod + directory: /exporters/prometheus labels: - dependencies - go @@ -236,9 +200,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /sdk/export/metric + - package-ecosystem: gomod + directory: /exporters/stdout/stdoutmetric labels: - dependencies - go @@ -246,9 +209,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /sdk/metric + - package-ecosystem: gomod + directory: /exporters/stdout/stdouttrace labels: - dependencies - go @@ -256,9 +218,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /internal/tools/semconv-gen + - package-ecosystem: gomod + directory: /exporters/zipkin labels: - dependencies - go @@ -266,9 +227,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/internal/retry + - package-ecosystem: gomod + directory: /internal/tools labels: - dependencies - go @@ -276,9 +236,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/otlptrace + - package-ecosystem: gomod + directory: /metric labels: - dependencies - go @@ -286,9 +245,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/otlptrace/otlptracegrpc + - package-ecosystem: gomod + directory: /schema labels: - dependencies - go @@ -296,9 +254,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/otlptrace/otlptracehttp + - package-ecosystem: gomod + directory: /sdk labels: - dependencies - go @@ -306,9 +263,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/otlpmetric + - package-ecosystem: gomod + directory: /sdk/export/metric labels: - dependencies - go @@ -316,9 +272,8 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp/otlpmetric/otlpmetricgrpc + - package-ecosystem: gomod + directory: /sdk/metric labels: - dependencies - go @@ -326,8 +281,7 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod + - package-ecosystem: gomod directory: /trace labels: - dependencies @@ -336,24 +290,3 @@ updates: 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 diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000000..c14b91aa4f1 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,32 @@ +name: Benchmark +on: + push: + branches: + - main +env: + DEFAULT_GO_VERSION: 1.16 +jobs: + benchmark: + name: Benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.4.0 + - uses: actions/setup-go@v2.2.0 + with: + go-version: ${{ env.DEFAULT_GO_VERSION }} + - name: Run benchmarks + run: make test-bench | tee output.txt + - name: Download previous benchmark data + uses: actions/cache@v2.1.7 + with: + path: ./benchmarks + key: ${{ runner.os }}-benchmark + - name: Store benchmarks result + uses: benchmark-action/github-action-benchmark@v1.13.0 + with: + name: Benchmarks + tool: 'go' + output-file-path: output.txt + external-data-json-path: ./benchmarks/data.json + auto-push: false + fail-on-alert: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09abecea0cb..3cb2893edcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.5 + uses: actions/setup-go@v2.2.0 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -40,7 +40,7 @@ jobs: - name: Run linters run: make dependabot-check license-check lint vanity-import-check - name: Build - run: make examples build + run: make build - name: Check clean repository run: make check-clean-work-tree @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.5 + uses: actions/setup-go@v2.2.0 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.5 + uses: actions/setup-go@v2.2.0 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -123,7 +123,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Install Go - uses: actions/setup-go@v2.1.5 + uses: actions/setup-go@v2.2.0 with: go-version: ${{ matrix.go-version }} - name: Checkout code diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 4ff7b99d7f3..b679cbaf7f9 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v2 with: ref: ${{ github.head_ref }} - - uses: actions/setup-go@v2.1.5 + - uses: actions/setup-go@v2.2.0 with: go-version: '^1.16.0' - uses: evantorrie/mott-the-tidier@v1-beta diff --git a/.gitignore b/.gitignore index 759cf53e002..99230bae28b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,5 @@ gen/ /example/opencensus/opencensus /example/passthrough/passthrough /example/prometheus/prometheus -/example/prom-collector/prom-collector /example/zipkin/zipkin /example/otel-collector/otel-collector diff --git a/.golangci.yml b/.golangci.yml index d40bdedc397..7a5fdc07abf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,11 +4,26 @@ run: tests: true #Default linters: + # Disable everything by default so upgrades to not include new "default + # enabled" linters. + disable-all: true + # Specifically enable linters we want to use. enable: - - misspell + - deadcode + - errcheck + - gofmt - goimports + - gosimple + - govet + - ineffassign + - misspell - revive - - gofmt + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + issues: exclude-rules: diff --git a/CHANGELOG.md b/CHANGELOG.md index 89df8a8c5a0..30b4333441d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,29 +8,77 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### ⚠️ Notice ⚠️ + +This update is a breaking change of the unstable Metrics API. Code instrumented with the `go.opentelemetry.io/otel/metric` <= v0.27.0 will need to be modified. + +### Added + +- Added support to configure the span limits with environment variables. + The following environment variables are used. (#2606) + - `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` + - `OTEL_SPAN_EVENT_COUNT_LIMIT` + - `OTEL_SPAN_LINK_COUNT_LIMIT` + + If the provided environment variables are invalid (negative), the default values would be used. +- Rename the `gc` runtime name to `go` (#2560) +- Log the Exporters configuration in the TracerProviders message. (#2578) + +### Changed + +- For tracestate's members, prepend the new element and remove the oldest one, which is over capacity (#2592) +- Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601) +- The metrics API has been significantly changed. (#2587) +- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` config. (#2639) +- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640) + +### Fixed + +- Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616) +- Use port `4318` instead of `4317` for default for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625) + +## [1.4.1] - 2022-02-16 + +### Fixed + +- Fix race condition in reading the dropped spans number for the `BatchSpanProcessor`. (#2615) + +## [1.4.0] - 2022-02-11 + ### Added -- Support `OTEL_EXPORTER_ZIPKIN_ENDPOINT` env to specify zipkin collector endpoint (#2490) -- Log the configuration of TracerProviders, and Tracers for debugging. To enable use a logger with Verbosity (V level) >=1 -- Added environment variables for: `OTEL_BSP_SCHEDULE_DELAY`, `OTEL_BSP_EXPORT_TIMEOUT`, `OTEL_BSP_MAX_QUEUE_SIZE` and `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` (#2515) +- Use `OTEL_EXPORTER_ZIPKIN_ENDPOINT` environment variable to specify zipkin collector endpoint. (#2490) +- Log the configuration of `TracerProvider`s, and `Tracer`s for debugging. + To enable use a logger with Verbosity (V level) `>=1`. (#2500) +- Added support to configure the batch span-processor with environment variables. + The following environment variables are used. (#2515) + - `OTEL_BSP_SCHEDULE_DELAY` + - `OTEL_BSP_EXPORT_TIMEOUT` + - `OTEL_BSP_MAX_QUEUE_SIZE`. + - `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` ### Changed -- Jaeger exporter takes into additional 70 bytes overhead into consideration when sending UDP packets (#2489, #2512) +- Zipkin exporter exports `Resource` attributes in the `Tags` field. (#2589) ### Deprecated -- Deprecate module `"go.opentelemetry.io/otel/sdk/export/metric"`, new functionality available in "go.opentelemetry.io/otel/sdk/metric" module: - - Import path changed `import "go.opentelemetry.io/otel/sdk/export/metric"` to `import go.opentelemetry.io/otel/sdk/metric/export` (#2382). -- Deprecate `AtomicFieldOffsets`, unnecessary public func (#2445) +- Deprecate module the `go.opentelemetry.io/otel/sdk/export/metric`. + Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2382) +- Deprecate `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets`. (#2445) ### Fixed -- Fixes the instrument kind for noop async instruments. (#2461) +- Fixed the instrument kind for noop async instruments to correctly report an implementation. (#2461) +- Fix UDP packets overflowing with Jaeger payloads. (#2489, #2512) - Change the `otlpmetric.Client` interface's `UploadMetrics` method to accept a single `ResourceMetrics` instead of a slice of them. (#2491) -- Specify explicit buckets in Prometheus example. (#2493) +- Specify explicit buckets in Prometheus example, fixing issue where example only has `+inf` bucket. (#2419, #2493) - W3C baggage will now decode urlescaped values. (#2529) - Baggage members are now only validated once, when calling `NewMember` and not also when adding it to the baggage itself. (#2522) +- The order attributes are dropped from spans in the `go.opentelemetry.io/otel/sdk/trace` package when capacity is reached is fixed to be in compliance with the OpenTelemetry specification. + Instead of dropping the least-recently-used attribute, the last added attribute is dropped. + This drop order still only applies to attributes with unique keys not already contained in the span. + If an attribute is added with a key already contained in the span, that attribute is updated to the new value being added. (#2576) ### Removed @@ -1670,7 +1718,9 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.3.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.4.1...HEAD +[1.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.1 +[1.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.0 [1.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.3.0 [1.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.2.0 [1.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.1.0 diff --git a/CODEOWNERS b/CODEOWNERS index 808755fe20c..76d959d237a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,6 +12,6 @@ # https://help.github.com/en/articles/about-code-owners # -* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared +* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared @hanyuancheung CODEOWNERS @MrAlias @Aneurysm9 @MadVikingGod diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7dead7084d3..098a7c54fbe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -508,6 +508,7 @@ Approvers: - [David Ashpole](https://github.com/dashpole), Google - [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep - [Robert Pająk](https://github.com/pellared), Splunk +- [Chester Cheung](https://github.com/hanyuancheung), Tencent Maintainers: diff --git a/Makefile b/Makefile index f360b56daf3..bcb3a6d8629 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,11 @@ # 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_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) +OTEL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(ALL_GO_MOD_DIRS)) ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort) GO = go @@ -27,8 +25,8 @@ 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 +precommit: dependabot-generate license-check misspell go-mod-tidy golangci-lint-fix test-default +ci: dependabot-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage # Tools @@ -49,6 +47,9 @@ $(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen CROSSLINK = $(TOOLS)/crosslink $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink +DBOTCONF = $(TOOLS)/dbotconf +$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/dbotconf + GOLANGCI_LINT = $(TOOLS)/golangci-lint $(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint @@ -68,55 +69,51 @@ 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) +tools: $(CROSSLINK) $(DBOTCONF) $(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) $(PORTO) - 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 +.PHONY: generate build + +generate: $(OTEL_GO_MOD_DIRS:%=generate/%) +generate/%: DIR=$* +generate/%: | $(STRINGER) $(PORTO) + @echo "$(GO) generate $(DIR)/..." \ + && cd $(DIR) \ + && PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && $(PORTO) -w . + +build: generate $(OTEL_GO_MOD_DIRS:%=build/%) $(OTEL_GO_MOD_DIRS:%=build-tests/%) +build/%: DIR=$* +build/%: + @echo "$(GO) build $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) build ./... + +build-tests/%: DIR=$* +build-tests/%: + @echo "$(GO) build tests $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null # Tests TEST_TARGETS := test-default test-bench test-short test-verbose test-race .PHONY: $(TEST_TARGETS) test -test-default: ARGS=-v -race +test-default test-race: ARGS=-race test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. test-short: ARGS=-short -test-verbose: ARGS=-v -test-race: ARGS=-race +test-verbose: ARGS=-v -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 +test: $(OTEL_GO_MOD_DIRS:%=test/%) +test/%: DIR=$* +test/%: + @echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS) COVERAGE_MODE = atomic COVERAGE_PROFILE = coverage.out @@ -134,32 +131,42 @@ test-coverage: | $(GOCOVMERGE) done; \ $(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt +.PHONY: golangci-lint golangci-lint-fix +golangci-lint-fix: ARGS=--fix +golangci-lint-fix: golangci-lint +golangci-lint: $(OTEL_GO_MOD_DIRS:%=golangci-lint/%) +golangci-lint/%: DIR=$* +golangci-lint/%: | $(GOLANGCI_LINT) + @echo 'golangci-lint $(if $(ARGS),$(ARGS) ,)$(DIR)' \ + && cd $(DIR) \ + && $(GOLANGCI_LINT) run --allow-serial-runners $(ARGS) + +.PHONY: crosslink +crosslink: | $(CROSSLINK) + @echo "cross-linking all go modules" \ + && $(CROSSLINK) + +.PHONY: go-mod-tidy +go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%) +go-mod-tidy/%: DIR=$* +go-mod-tidy/%: | crosslink + @echo "$(GO) mod tidy in $(DIR)" \ + && cd $(DIR) \ + && $(GO) mod tidy + +.PHONY: lint-modules +lint-modules: go-mod-tidy + .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 +lint: misspell lint-modules golangci-lint .PHONY: vanity-import-check vanity-import-check: | $(PORTO) - $(PORTO) --include-internal -l . + @$(PORTO) --include-internal -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) + @$(MISSPELL) -w $(ALL_DOCS) .PHONY: license-check license-check: @@ -171,20 +178,27 @@ license-check: exit 1; \ fi +DEPENDABOT_PATH=./.github/dependabot.yml .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 \ + do grep -q "directory: \+$$f" $(DEPENDABOT_PATH) \ || 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"; \ + echo "missing dependabot entry:"; echo "$$result"; \ + echo "new modules need to be added to the $(DEPENDABOT_PATH) file"; \ + echo "(run: make dependabot-generate)"; \ exit 1; \ fi +.PHONY: dependabot-generate +dependabot-generate: $(DBOTCONF) + @echo "gerating dependabot configuration"; \ + $(DBOTCONF) + .PHONY: check-clean-work-tree check-clean-work-tree: @if ! git diff --quiet; then \ diff --git a/attribute/kv.go b/attribute/kv.go index 8f579338554..1ddf3ce0580 100644 --- a/attribute/kv.go +++ b/attribute/kv.go @@ -26,7 +26,7 @@ type KeyValue struct { // Valid returns if kv is a valid OpenTelemetry attribute. func (kv KeyValue) Valid() bool { - return kv.Key != "" && kv.Value.Type() != INVALID + return kv.Key.Defined() && kv.Value.Type() != INVALID } // Bool creates a KeyValue with a BOOL Value type. diff --git a/bridge/opencensus/exporter.go b/bridge/opencensus/exporter.go index e497d95150d..d40ddc9d665 100644 --- a/bridge/opencensus/exporter.go +++ b/bridge/opencensus/exporter.go @@ -27,8 +27,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" - + "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/export" @@ -171,17 +170,17 @@ func convertDescriptor(ocDescriptor metricdata.Descriptor) (sdkapi.Descriptor, e // Includes TypeGaugeDistribution, TypeCumulativeDistribution, TypeSummary return sdkapi.Descriptor{}, fmt.Errorf("%w; descriptor type: %v", errConversion, ocDescriptor.Type) } - opts := []metric.InstrumentOption{ - metric.WithDescription(ocDescriptor.Description), + opts := []instrument.Option{ + instrument.WithDescription(ocDescriptor.Description), } switch ocDescriptor.Unit { case metricdata.UnitDimensionless: - opts = append(opts, metric.WithUnit(unit.Dimensionless)) + opts = append(opts, instrument.WithUnit(unit.Dimensionless)) case metricdata.UnitBytes: - opts = append(opts, metric.WithUnit(unit.Bytes)) + opts = append(opts, instrument.WithUnit(unit.Bytes)) case metricdata.UnitMilliseconds: - opts = append(opts, metric.WithUnit(unit.Milliseconds)) + opts = append(opts, instrument.WithUnit(unit.Milliseconds)) } - cfg := metric.NewInstrumentConfig(opts...) + cfg := instrument.NewConfig(opts...) return sdkapi.NewDescriptor(ocDescriptor.Name, ikind, nkind, cfg.Description(), cfg.Unit()), nil } diff --git a/bridge/opencensus/exporter_test.go b/bridge/opencensus/exporter_test.go index 75a43a64fa0..79e195c1f6c 100644 --- a/bridge/opencensus/exporter_test.go +++ b/bridge/opencensus/exporter_test.go @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" @@ -400,8 +400,8 @@ func TestConvertDescriptor(t *testing.T) { "foo", sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, - metric.WithDescription("bar"), - metric.WithUnit(unit.Bytes), + instrument.WithDescription("bar"), + instrument.WithUnit(unit.Bytes), ), }, { @@ -416,8 +416,8 @@ func TestConvertDescriptor(t *testing.T) { "foo", sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, - metric.WithDescription("bar"), - metric.WithUnit(unit.Milliseconds), + instrument.WithDescription("bar"), + instrument.WithUnit(unit.Milliseconds), ), }, { @@ -432,8 +432,8 @@ func TestConvertDescriptor(t *testing.T) { "foo", sdkapi.CounterObserverInstrumentKind, number.Int64Kind, - metric.WithDescription("bar"), - metric.WithUnit(unit.Dimensionless), + instrument.WithDescription("bar"), + instrument.WithUnit(unit.Dimensionless), ), }, { @@ -448,8 +448,8 @@ func TestConvertDescriptor(t *testing.T) { "foo", sdkapi.CounterObserverInstrumentKind, number.Float64Kind, - metric.WithDescription("bar"), - metric.WithUnit(unit.Dimensionless), + instrument.WithDescription("bar"), + instrument.WithUnit(unit.Dimensionless), ), }, { diff --git a/bridge/opencensus/go.mod b/bridge/opencensus/go.mod index 30a69366c61..db2733fc484 100644 --- a/bridge/opencensus/go.mod +++ b/bridge/opencensus/go.mod @@ -4,11 +4,11 @@ go 1.16 require ( go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel => ../.. @@ -25,8 +25,6 @@ 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 diff --git a/bridge/opencensus/test/go.mod b/bridge/opencensus/test/go.mod index 95c30b75eaa..b262f82d5e8 100644 --- a/bridge/opencensus/test/go.mod +++ b/bridge/opencensus/test/go.mod @@ -4,10 +4,10 @@ go 1.16 require ( go.opencensus.io v0.23.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/bridge/opencensus v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/bridge/opencensus v0.27.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel => ../../.. diff --git a/bridge/opentracing/go.mod b/bridge/opentracing/go.mod index fb9ff78019a..6a36fde8822 100644 --- a/bridge/opentracing/go.mod +++ b/bridge/opentracing/go.mod @@ -6,8 +6,8 @@ replace go.opentelemetry.io/otel => ../.. require ( github.com/opentracing/opentracing-go v1.2.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../opencensus @@ -22,8 +22,6 @@ 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 diff --git a/example/fib/go.mod b/example/fib/go.mod index 6c36e2a64a8..f32a17d8ed9 100644 --- a/example/fib/go.mod +++ b/example/fib/go.mod @@ -3,10 +3,10 @@ module go.opentelemetry.io/otel/example/fib go 1.16 require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel => ../.. diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index af7f7f20b68..c67a139299d 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -9,9 +9,9 @@ replace ( ) require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/jaeger v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/jaeger v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -26,8 +26,6 @@ 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 diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index a9452a19c75..3873edf2951 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -9,10 +9,10 @@ replace ( require ( github.com/go-logr/stdr v1.2.2 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -27,8 +27,6 @@ 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 diff --git a/example/opencensus/go.mod b/example/opencensus/go.mod index a1fb87122a0..ba0fa9db3de 100644 --- a/example/opencensus/go.mod +++ b/example/opencensus/go.mod @@ -10,12 +10,12 @@ replace ( require ( go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/bridge/opencensus v0.26.0 - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.26.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/bridge/opencensus v0.27.1 + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.27.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 ) replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing @@ -28,8 +28,6 @@ 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 diff --git a/example/otel-collector/Makefile b/example/otel-collector/Makefile index 606e8f86ef4..47c560b34c8 100644 --- a/example/otel-collector/Makefile +++ b/example/otel-collector/Makefile @@ -3,15 +3,7 @@ namespace-k8s: 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 + kubectl create -n observability -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.31.0/jaeger-operator.yaml jaeger-k8s: kubectl apply -f k8s/jaeger.yaml @@ -31,11 +23,4 @@ clean-k8s: - 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 + - kubectl delete -n observability -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.31.0/jaeger-operator.yaml diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index acbc10cd1bb..bc705fd9bac 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -8,10 +8,10 @@ replace ( ) require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 google.golang.org/grpc v1.44.0 ) @@ -27,8 +27,6 @@ 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 diff --git a/example/passthrough/go.mod b/example/passthrough/go.mod index 0c023e59c36..b738a24061b 100644 --- a/example/passthrough/go.mod +++ b/example/passthrough/go.mod @@ -3,10 +3,10 @@ module go.opentelemetry.io/otel/example/passthrough go 1.16 require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace ( @@ -29,8 +29,6 @@ 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 diff --git a/example/prometheus/go.mod b/example/prometheus/go.mod index 62bb10691ee..b30c60e547f 100644 --- a/example/prometheus/go.mod +++ b/example/prometheus/go.mod @@ -9,10 +9,10 @@ replace ( ) require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/prometheus v0.26.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/prometheus v0.27.0 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk/metric v0.27.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -27,8 +27,6 @@ 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 diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 8a1248a358e..12148d2595f 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -25,7 +25,7 @@ import ( "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/metric/instrument" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/export/aggregation" @@ -35,6 +35,9 @@ import ( var ( lemonsKey = attribute.Key("ex.com/lemons") + + // TODO Bring back Global package + meterProvider metric.MeterProvider ) func initMeter() { @@ -54,7 +57,9 @@ func initMeter() { if err != nil { log.Panicf("failed to initialize prometheus exporter %v", err) } - global.SetMeterProvider(exporter.MeterProvider()) + // TODO Bring back Global package + // global.SetMeterProvider(exporter.MeterProvider()) + meterProvider = exporter.MeterProvider() http.HandleFunc("/", exporter.ServeHTTP) go func() { @@ -67,23 +72,33 @@ func initMeter() { func main() { initMeter() - meter := global.Meter("ex.com/basic") + // TODO Bring back Global package + // meter := global.Meter("ex.com/basic") + meter := meterProvider.Meter("ex.com/basic") observerLock := new(sync.RWMutex) observerValueToReport := new(float64) observerLabelsToReport := new([]attribute.KeyValue) - cb := func(_ context.Context, result metric.Float64ObserverResult) { + + gaugeObserver, err := meter.AsyncFloat64().Gauge("ex.com.one") + if err != nil { + log.Panicf("failed to initialize instrument: %v", err) + } + _ = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) { (*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"), - ) + gaugeObserver.Observe(ctx, value, labels...) + }) - histogram := metric.Must(meter).NewFloat64Histogram("ex.com.two") - counter := metric.Must(meter).NewFloat64Counter("ex.com.three") + histogram, err := meter.SyncFloat64().Histogram("ex.com.two") + if err != nil { + log.Panicf("failed to initialize instrument: %v", err) + } + counter, err := meter.SyncFloat64().Counter("ex.com.three") + if err != nil { + log.Panicf("failed to initialize instrument: %v", err) + } commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")} notSoCommonLabels := []attribute.KeyValue{lemonsKey.Int(13)} @@ -94,12 +109,9 @@ func main() { *observerValueToReport = 1.0 *observerLabelsToReport = commonLabels (*observerLock).Unlock() - meter.RecordBatch( - ctx, - commonLabels, - histogram.Measurement(2.0), - counter.Measurement(12.0), - ) + + histogram.Record(ctx, 2.0, commonLabels...) + counter.Add(ctx, 12.0, commonLabels...) time.Sleep(5 * time.Second) @@ -107,12 +119,8 @@ func main() { *observerValueToReport = 1.0 *observerLabelsToReport = notSoCommonLabels (*observerLock).Unlock() - meter.RecordBatch( - ctx, - notSoCommonLabels, - histogram.Measurement(2.0), - counter.Measurement(22.0), - ) + histogram.Record(ctx, 2.0, notSoCommonLabels...) + counter.Add(ctx, 22.0, notSoCommonLabels...) time.Sleep(5 * time.Second) @@ -120,12 +128,8 @@ func main() { *observerValueToReport = 13.0 *observerLabelsToReport = commonLabels (*observerLock).Unlock() - meter.RecordBatch( - ctx, - commonLabels, - histogram.Measurement(12.0), - counter.Measurement(13.0), - ) + histogram.Record(ctx, 12.0, commonLabels...) + counter.Add(ctx, 13.0, commonLabels...) fmt.Println("Example finished updating, please visit :2222") diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index da82762c21e..c47de71dfda 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -9,10 +9,10 @@ replace ( ) require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/zipkin v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/zipkin v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -27,8 +27,6 @@ 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 => ./ diff --git a/exporters/jaeger/go.mod b/exporters/jaeger/go.mod index ce530fbdfa2..265bbc92eb3 100644 --- a/exporters/jaeger/go.mod +++ b/exporters/jaeger/go.mod @@ -5,9 +5,9 @@ go 1.16 require ( github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -22,8 +22,6 @@ 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 diff --git a/exporters/jaeger/jaeger.go b/exporters/jaeger/jaeger.go index 22a5ed1afa8..d87b79621ef 100644 --- a/exporters/jaeger/jaeger.go +++ b/exporters/jaeger/jaeger.go @@ -123,6 +123,15 @@ func (e *Exporter) Shutdown(ctx context.Context) error { return e.uploader.shutdown(ctx) } +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (e *Exporter) MarshalLog() interface{} { + return struct { + Type string + }{ + Type: "jaeger", + } +} + func spanToThrift(ss sdktrace.ReadOnlySpan) *gen.Span { attr := ss.Attributes() tags := make([]*gen.Tag, 0, len(attr)) diff --git a/exporters/otlp/internal/config.go b/exporters/otlp/internal/config.go new file mode 100644 index 00000000000..b3fd45d9d31 --- /dev/null +++ b/exporters/otlp/internal/config.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package internal contains common functionality for all OTLP exporters. +package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal" + +import ( + "fmt" + "path" + "strings" +) + +// CleanPath returns a path with all spaces trimmed and all redundancies removed. If urlPath is empty or cleaning it results in an empty string, defaultPath is returned instead. +func CleanPath(urlPath string, defaultPath string) string { + tmp := path.Clean(strings.TrimSpace(urlPath)) + if tmp == "." { + return defaultPath + } + if !path.IsAbs(tmp) { + tmp = fmt.Sprintf("/%s", tmp) + } + return tmp +} diff --git a/exporters/otlp/internal/config_test.go b/exporters/otlp/internal/config_test.go new file mode 100644 index 00000000000..92a819a1f2b --- /dev/null +++ b/exporters/otlp/internal/config_test.go @@ -0,0 +1,83 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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 "testing" + +func TestCleanPath(t *testing.T) { + type args struct { + urlPath string + defaultPath string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "clean empty path", + args: args{ + urlPath: "", + defaultPath: "DefaultPath", + }, + want: "DefaultPath", + }, + { + name: "clean metrics path", + args: args{ + urlPath: "/prefix/v1/metrics", + defaultPath: "DefaultMetricsPath", + }, + want: "/prefix/v1/metrics", + }, + { + name: "clean traces path", + args: args{ + urlPath: "https://env_endpoint", + defaultPath: "DefaultTracesPath", + }, + want: "/https:/env_endpoint", + }, + { + name: "spaces trimmed", + args: args{ + urlPath: " /dir", + }, + want: "/dir", + }, + { + name: "clean path empty", + args: args{ + urlPath: "dir/..", + defaultPath: "DefaultTracesPath", + }, + want: "DefaultTracesPath", + }, + { + name: "make absolute", + args: args{ + urlPath: "dir/a", + }, + want: "/dir/a", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CleanPath(tt.args.urlPath, tt.args.defaultPath); got != tt.want { + t.Errorf("CleanPath() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/exporters/otlp/otlpmetric/go.mod b/exporters/otlp/otlpmetric/go.mod index ba19e06432d..3a1567c0ec2 100644 --- a/exporters/otlp/otlpmetric/go.mod +++ b/exporters/otlp/otlpmetric/go.mod @@ -5,11 +5,11 @@ go 1.16 require ( github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 go.opentelemetry.io/proto/otlp v0.12.0 google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 diff --git a/exporters/otlp/otlpmetric/internal/otlpconfig/options.go b/exporters/otlp/otlpmetric/internal/otlpconfig/options.go index fed8a82f1c7..f2c8ee5d21a 100644 --- a/exporters/otlp/otlpmetric/internal/otlpconfig/options.go +++ b/exporters/otlp/otlpmetric/internal/otlpconfig/options.go @@ -25,6 +25,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/encoding/gzip" + "go.opentelemetry.io/otel/exporters/otlp/internal" "go.opentelemetry.io/otel/exporters/otlp/internal/retry" ) @@ -72,24 +73,38 @@ type ( } ) -func NewDefaultConfig() Config { - c := Config{ +// NewHTTPConfig returns a new Config with all settings applied from opts and +// any unset setting using the default HTTP config values. +func NewHTTPConfig(opts ...HTTPOption) Config { + cfg := Config{ Metrics: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorPort), + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), URLPath: DefaultMetricsPath, Compression: NoCompression, Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, } - - return c + cfg = ApplyHTTPEnvConfigs(cfg) + for _, opt := range opts { + cfg = opt.ApplyHTTPOption(cfg) + } + cfg.Metrics.URLPath = internal.CleanPath(cfg.Metrics.URLPath, DefaultMetricsPath) + return cfg } // NewGRPCConfig returns a new Config with all settings applied from opts and // any unset setting using the default gRPC config values. func NewGRPCConfig(opts ...GRPCOption) Config { - cfg := NewDefaultConfig() + cfg := Config{ + Metrics: SignalConfig{ + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), + URLPath: DefaultMetricsPath, + Compression: NoCompression, + Timeout: DefaultTimeout, + }, + RetryConfig: retry.DefaultConfig, + } cfg = ApplyGRPCEnvConfigs(cfg) for _, opt := range opts { cfg = opt.ApplyGRPCOption(cfg) diff --git a/exporters/otlp/otlpmetric/internal/otlpconfig/options_test.go b/exporters/otlp/otlpmetric/internal/otlpconfig/options_test.go index d82802603e3..44c9af4d94c 100644 --- a/exporters/otlp/otlpmetric/internal/otlpconfig/options_test.go +++ b/exporters/otlp/otlpmetric/internal/otlpconfig/options_test.go @@ -76,7 +76,11 @@ func TestConfigs(t *testing.T) { { name: "Test default configs", asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, "localhost:4317", c.Metrics.Endpoint) + if grpcOption { + assert.Equal(t, "localhost:4317", c.Metrics.Endpoint) + } else { + assert.Equal(t, "localhost:4318", c.Metrics.Endpoint) + } assert.Equal(t, otlpconfig.NoCompression, c.Metrics.Compression) assert.Equal(t, map[string]string(nil), c.Metrics.Headers) assert.Equal(t, 10*time.Second, c.Metrics.Timeout) @@ -386,11 +390,7 @@ func TestConfigs(t *testing.T) { t.Cleanup(func() { otlpconfig.DefaultEnvOptionsReader = origEOR }) // Tests Generic options as HTTP Options - cfg := otlpconfig.NewDefaultConfig() - cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg) - for _, opt := range tt.opts { - cfg = opt.ApplyHTTPOption(cfg) - } + cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(tt.opts)...) tt.asserts(t, &cfg, false) // Tests Generic options as gRPC Options @@ -400,6 +400,14 @@ func TestConfigs(t *testing.T) { } } +func asHTTPOptions(opts []otlpconfig.GenericOption) []otlpconfig.HTTPOption { + converted := make([]otlpconfig.HTTPOption, len(opts)) + for i, o := range opts { + converted[i] = otlpconfig.NewHTTPOption(o.ApplyHTTPOption) + } + return converted +} + func asGRPCOptions(opts []otlpconfig.GenericOption) []otlpconfig.GRPCOption { converted := make([]otlpconfig.GRPCOption, len(opts)) for i, o := range opts { diff --git a/exporters/otlp/otlpmetric/internal/otlpconfig/optiontypes.go b/exporters/otlp/otlpmetric/internal/otlpconfig/optiontypes.go index beb0eb08b3b..1d6f83f366d 100644 --- a/exporters/otlp/otlpmetric/internal/otlpconfig/optiontypes.go +++ b/exporters/otlp/otlpmetric/internal/otlpconfig/optiontypes.go @@ -17,9 +17,10 @@ package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric import "time" const ( - // DefaultCollectorPort is the port the Exporter will attempt connect to - // if no collector port is provided. - DefaultCollectorPort uint16 = 4317 + // DefaultCollectorGRPCPort is the default gRPC port of the collector. + DefaultCollectorGRPCPort uint16 = 4317 + // DefaultCollectorHTTPPort is the default HTTP port of the collector. + DefaultCollectorHTTPPort uint16 = 4318 // DefaultCollectorHost is the host address the Exporter will attempt // connect to if no collector address is provided. DefaultCollectorHost string = "localhost" diff --git a/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go b/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go index f7cd1937aa6..524cb774588 100644 --- a/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go +++ b/exporters/otlp/otlpmetric/internal/otlpmetrictest/otlptest.go @@ -25,7 +25,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" - "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/instrument" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/number" @@ -63,34 +63,37 @@ func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlpmetric.Exporter case sdkapi.CounterInstrumentKind: switch data.nKind { case number.Int64Kind: - metric.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels...) + c, _ := meter.SyncInt64().Counter(name) + c.Add(ctx, data.val, labels...) case number.Float64Kind: - metric.Must(meter).NewFloat64Counter(name).Add(ctx, float64(data.val), labels...) + c, _ := meter.SyncFloat64().Counter(name) + c.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...) + c, _ := meter.SyncInt64().Histogram(name) + c.Record(ctx, data.val, labels...) case number.Float64Kind: - metric.Must(meter).NewFloat64Histogram(name).Record(ctx, float64(data.val), labels...) + c, _ := meter.SyncFloat64().Histogram(name) + c.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...) - }, - ) + g, _ := meter.AsyncInt64().Gauge(name) + _ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) { + g.Observe(ctx, 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) + g, _ := meter.AsyncFloat64().Gauge(name) + _ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) { + g.Observe(ctx, float64(data.val), labels...) + }) default: assert.Failf(t, "unsupported number testing kind", data.nKind.String()) } diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go index bc979541c90..dc7eeda51d9 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go @@ -93,7 +93,7 @@ func newGRPCExporter(t *testing.T, ctx context.Context, endpoint string, additio } func newExporterEndToEndTest(t *testing.T, additionalOpts []otlpmetricgrpc.Option) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") + mc := runMockCollector(t) defer func() { _ = mc.stop() @@ -115,7 +115,7 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlpmetricgrpc.Optio } func TestExporterShutdown(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") + mc := runMockCollector(t) defer func() { _ = mc.Stop() }() @@ -296,7 +296,7 @@ func TestStartErrorInvalidAddress(t *testing.T) { } func TestEmptyData(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") + mc := runMockCollector(t) defer func() { _ = mc.stop() @@ -314,7 +314,7 @@ func TestEmptyData(t *testing.T) { } func TestFailedMetricTransform(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") + mc := runMockCollector(t) defer func() { _ = mc.stop() diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go index f37c39095f7..fe0866b7af5 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go @@ -24,8 +24,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/metric/instrument" 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" @@ -54,7 +53,8 @@ func Example_insecure() { controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) - global.SetMeterProvider(pusher) + // TODO Bring back Global package + // global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) @@ -68,14 +68,16 @@ func Example_insecure() { } }() - meter := global.Meter("test-meter") + // TODO Bring Back Global package + // meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") + meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") // Recorder metric example - counter := metric.Must(meter). - NewFloat64Counter( - "an_important_metric", - metric.WithDescription("Measures the cumulative epicness of the app"), - ) + + counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app")) + if err != nil { + log.Fatalf("Failed to create the instrument: %v", err) + } for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) @@ -113,7 +115,8 @@ func Example_withTLS() { controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) - global.SetMeterProvider(pusher) + // TODO Bring back Global package + // global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) @@ -128,14 +131,15 @@ func Example_withTLS() { } }() - meter := global.Meter("test-meter") + // TODO Bring back Global package + // meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") + meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") // Recorder metric example - counter := metric.Must(meter). - NewFloat64Counter( - "an_important_metric", - metric.WithDescription("Measures the cumulative epicness of the app"), - ) + counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app")) + if err != nil { + log.Fatalf("Failed to create the instrument: %v", err) + } for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) @@ -170,7 +174,8 @@ func Example_withDifferentSignalCollectors() { controller.WithExporter(exp), controller.WithCollectPeriod(2*time.Second), ) - global.SetMeterProvider(pusher) + // TODO Bring back Global package + // global.SetMeterProvider(pusher) if err := pusher.Start(ctx); err != nil { log.Fatalf("could not start metric controoler: %v", err) @@ -184,14 +189,15 @@ func Example_withDifferentSignalCollectors() { } }() - meter := global.Meter("test-meter") + // TODO Bring back Global package + // meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") + meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test") // Recorder metric example - counter := metric.Must(meter). - NewFloat64Counter( - "an_important_metric", - metric.WithDescription("Measures the cumulative epicness of the app"), - ) + counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app")) + if err != nil { + log.Fatalf("Failed to create the instrument: %v", err) + } for i := 0; i < 10; i++ { log.Printf("Doing really hard work (%d / 10)\n", i+1) diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod index 11fe57e8a7c..cba505c7944 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod @@ -4,12 +4,12 @@ go 1.16 require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 go.opentelemetry.io/proto/otlp v0.12.0 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.44.0 @@ -42,8 +42,6 @@ replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/o 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 diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/client.go b/exporters/otlp/otlpmetric/otlpmetrichttp/client.go index aa381f8e038..dab28ff8fb2 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/client.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/client.go @@ -24,9 +24,7 @@ import ( "net" "net/http" "net/url" - "path" "strconv" - "strings" "sync" "time" @@ -77,26 +75,7 @@ type client struct { // NewClient creates a new HTTP metric client. func NewClient(opts ...Option) otlpmetric.Client { - cfg := otlpconfig.NewDefaultConfig() - cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg) - for _, opt := range opts { - cfg = opt.applyHTTPOption(cfg) - } - - for pathPtr, defaultPath := range map[*string]string{ - &cfg.Metrics.URLPath: otlpconfig.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 - } + cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(opts)...) httpClient := &http.Client{ Transport: ourTransport, diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod index 810db0a898c..a91939eff01 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod @@ -4,9 +4,9 @@ go 1.16 require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 go.opentelemetry.io/proto/otlp v0.12.0 google.golang.org/protobuf v1.27.1 ) @@ -37,14 +37,10 @@ replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/o 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 @@ -53,10 +49,6 @@ replace go.opentelemetry.io/otel/exporters/otlp/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 diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/options.go b/exporters/otlp/otlpmetric/otlpmetrichttp/options.go index bec26204c74..8d12c791954 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/options.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/options.go @@ -40,6 +40,14 @@ type Option interface { applyHTTPOption(otlpconfig.Config) otlpconfig.Config } +func asHTTPOptions(opts []Option) []otlpconfig.HTTPOption { + converted := make([]otlpconfig.HTTPOption, len(opts)) + for i, o := range opts { + converted[i] = otlpconfig.NewHTTPOption(o.applyHTTPOption) + } + return converted +} + // RetryConfig defines configuration for retrying batches in case of export // failure using an exponential backoff. type RetryConfig retry.Config @@ -52,11 +60,10 @@ func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config return 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. +// 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:4318). Note that the endpoint must not +// contain any URL path. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} } diff --git a/exporters/otlp/otlptrace/README.md b/exporters/otlp/otlptrace/README.md index 8a40a86a246..ca91fd4f489 100644 --- a/exporters/otlp/otlptrace/README.md +++ b/exporters/otlp/otlptrace/README.md @@ -38,12 +38,14 @@ override the default configuration. For more information about how each of these environment variables is interpreted, see [the OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/protocol/exporter.md). -| 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` | +| Environment variable | Option | Default value | +| ------------------------------------------------------------------------ |------------------------------ | -------------------------------------------------------- | +| `OTEL_EXPORTER_OTLP_ENDPOINT` `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `WithEndpoint` `WithInsecure` | `https://localhost:4317` or `https://localhost:4318`[^1] | +| `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` | + +[^1]: The gRPC client defaults to `https://localhost:4317` and the HTTP client `https://localhost:4318`. Configuration using options have precedence over the environment variables. diff --git a/exporters/otlp/otlptrace/exporter.go b/exporters/otlp/otlptrace/exporter.go index 7e9bb6c47ae..c5ee6c098cc 100644 --- a/exporters/otlp/otlptrace/exporter.go +++ b/exporters/otlp/otlptrace/exporter.go @@ -100,3 +100,14 @@ func NewUnstarted(client Client) *Exporter { client: client, } } + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (e *Exporter) MarshalLog() interface{} { + return struct { + Type string + Client Client + }{ + Type: "otlptrace", + Client: e.client, + } +} diff --git a/exporters/otlp/otlptrace/go.mod b/exporters/otlp/otlptrace/go.mod index ddc8f8a5ed8..dd9eb1815aa 100644 --- a/exporters/otlp/otlptrace/go.mod +++ b/exporters/otlp/otlptrace/go.mod @@ -5,10 +5,10 @@ go 1.16 require ( github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 go.opentelemetry.io/proto/otlp v0.12.0 google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 @@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencens 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 diff --git a/exporters/otlp/otlptrace/internal/otlpconfig/options.go b/exporters/otlp/otlptrace/internal/otlpconfig/options.go index e6fb14e00e8..56e83b85334 100644 --- a/exporters/otlp/otlptrace/internal/otlpconfig/options.go +++ b/exporters/otlp/otlptrace/internal/otlpconfig/options.go @@ -25,6 +25,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/encoding/gzip" + "go.opentelemetry.io/otel/exporters/otlp/internal" "go.opentelemetry.io/otel/exporters/otlp/internal/retry" ) @@ -65,24 +66,38 @@ type ( } ) -func NewDefaultConfig() Config { - c := Config{ +// NewHTTPConfig returns a new Config with all settings applied from opts and +// any unset setting using the default HTTP config values. +func NewHTTPConfig(opts ...HTTPOption) Config { + cfg := Config{ Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorPort), + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), URLPath: DefaultTracesPath, Compression: NoCompression, Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, } - - return c + cfg = ApplyHTTPEnvConfigs(cfg) + for _, opt := range opts { + cfg = opt.ApplyHTTPOption(cfg) + } + cfg.Traces.URLPath = internal.CleanPath(cfg.Traces.URLPath, DefaultTracesPath) + return cfg } // NewGRPCConfig returns a new Config with all settings applied from opts and // any unset setting using the default gRPC config values. func NewGRPCConfig(opts ...GRPCOption) Config { - cfg := NewDefaultConfig() + cfg := Config{ + Traces: SignalConfig{ + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), + URLPath: DefaultTracesPath, + Compression: NoCompression, + Timeout: DefaultTimeout, + }, + RetryConfig: retry.DefaultConfig, + } cfg = ApplyGRPCEnvConfigs(cfg) for _, opt := range opts { cfg = opt.ApplyGRPCOption(cfg) diff --git a/exporters/otlp/otlptrace/internal/otlpconfig/options_test.go b/exporters/otlp/otlptrace/internal/otlpconfig/options_test.go index b12e0bd9a5c..4efa2f7c630 100644 --- a/exporters/otlp/otlptrace/internal/otlpconfig/options_test.go +++ b/exporters/otlp/otlptrace/internal/otlpconfig/options_test.go @@ -76,7 +76,11 @@ func TestConfigs(t *testing.T) { { name: "Test default configs", asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { - assert.Equal(t, "localhost:4317", c.Traces.Endpoint) + if grpcOption { + assert.Equal(t, "localhost:4317", c.Traces.Endpoint) + } else { + assert.Equal(t, "localhost:4318", 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) @@ -384,11 +388,7 @@ func TestConfigs(t *testing.T) { t.Cleanup(func() { otlpconfig.DefaultEnvOptionsReader = origEOR }) // Tests Generic options as HTTP Options - cfg := otlpconfig.NewDefaultConfig() - cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg) - for _, opt := range tt.opts { - cfg = opt.ApplyHTTPOption(cfg) - } + cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(tt.opts)...) tt.asserts(t, &cfg, false) // Tests Generic options as gRPC Options @@ -398,6 +398,14 @@ func TestConfigs(t *testing.T) { } } +func asHTTPOptions(opts []otlpconfig.GenericOption) []otlpconfig.HTTPOption { + converted := make([]otlpconfig.HTTPOption, len(opts)) + for i, o := range opts { + converted[i] = otlpconfig.NewHTTPOption(o.ApplyHTTPOption) + } + return converted +} + func asGRPCOptions(opts []otlpconfig.GenericOption) []otlpconfig.GRPCOption { converted := make([]otlpconfig.GRPCOption, len(opts)) for i, o := range opts { diff --git a/exporters/otlp/otlptrace/internal/otlpconfig/optiontypes.go b/exporters/otlp/otlptrace/internal/otlpconfig/optiontypes.go index f69e31095d2..d4331e8749f 100644 --- a/exporters/otlp/otlptrace/internal/otlpconfig/optiontypes.go +++ b/exporters/otlp/otlptrace/internal/otlpconfig/optiontypes.go @@ -15,9 +15,10 @@ 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 + // DefaultCollectorGRPCPort is the default gRPC port of the collector. + DefaultCollectorGRPCPort uint16 = 4317 + // DefaultCollectorHTTPPort is the default HTTP port of the collector. + DefaultCollectorHTTPPort uint16 = 4318 // DefaultCollectorHost is the host address the Exporter will attempt // connect to if no collector address is provided. DefaultCollectorHost string = "localhost" diff --git a/exporters/otlp/otlptrace/internal/tracetransform/span.go b/exporters/otlp/otlptrace/internal/tracetransform/span.go index 2f0f5eacb77..88c05912f0d 100644 --- a/exporters/otlp/otlptrace/internal/tracetransform/span.go +++ b/exporters/otlp/otlptrace/internal/tracetransform/span.go @@ -23,10 +23,6 @@ import ( tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) -const ( - maxEventsPerSpan = 128 -) - // Spans transforms a slice of OpenTelemetry spans into a slice of OTLP // ResourceSpans. func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans { @@ -162,9 +158,10 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link { sid := otLink.SpanContext.SpanID() sl = append(sl, &tracepb.Span_Link{ - TraceId: tid[:], - SpanId: sid[:], - Attributes: KeyValues(otLink.Attributes), + TraceId: tid[:], + SpanId: sid[:], + Attributes: KeyValues(otLink.Attributes), + DroppedAttributesCount: uint32(otLink.DroppedAttributeCount), }) } return sl @@ -176,29 +173,16 @@ func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event { return nil } - evCount := len(es) - if evCount > maxEventsPerSpan { - evCount = maxEventsPerSpan - } - events := make([]*tracepb.Span_Event, 0, evCount) - nEvents := 0 - + events := make([]*tracepb.Span_Event, len(es)) // Transform message events - for _, e := range es { - if nEvents >= maxEventsPerSpan { - break + for i := 0; i < len(es); i++ { + events[i] = &tracepb.Span_Event{ + Name: es[i].Name, + TimeUnixNano: uint64(es[i].Time.UnixNano()), + Attributes: KeyValues(es[i].Attributes), + DroppedAttributesCount: uint32(es[i].DroppedAttributeCount), } - 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 } diff --git a/exporters/otlp/otlptrace/internal/tracetransform/span_test.go b/exporters/otlp/otlptrace/internal/tracetransform/span_test.go index 351de0367ef..37860d2e047 100644 --- a/exporters/otlp/otlptrace/internal/tracetransform/span_test.go +++ b/exporters/otlp/otlptrace/internal/tracetransform/span_test.go @@ -15,7 +15,6 @@ package tracetransform import ( - "strconv" "testing" "time" @@ -87,9 +86,10 @@ func TestSpanEvent(t *testing.T) { Time: eventTime, }, { - Name: "test 2", - Attributes: attrs, - Time: eventTime, + Name: "test 2", + Attributes: attrs, + Time: eventTime, + DroppedAttributeCount: 2, }, }) if !assert.Len(t, got, 2) { @@ -98,19 +98,7 @@ func TestSpanEvent(t *testing.T) { 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) + assert.Equal(t, &tracepb.Span_Event{Name: "test 2", Attributes: KeyValues(attrs), TimeUnixNano: eventTimestamp, DroppedAttributesCount: 2}, got[1]) } func TestNilLinks(t *testing.T) { @@ -124,10 +112,13 @@ func TestEmptyLinks(t *testing.T) { func TestLinks(t *testing.T) { attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} l := []tracesdk.Link{ - {}, { - SpanContext: trace.SpanContext{}, - Attributes: attrs, + DroppedAttributeCount: 3, + }, + { + SpanContext: trace.SpanContext{}, + Attributes: attrs, + DroppedAttributeCount: 3, }, } got := links(l) @@ -139,8 +130,9 @@ func TestLinks(t *testing.T) { // 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}, + 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}, + DroppedAttributesCount: 3, } assert.Equal(t, expected, got[0]) @@ -151,6 +143,7 @@ func TestLinks(t *testing.T) { // 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]) + assert.Equal(t, l[1].DroppedAttributeCount, int(got[1].DroppedAttributesCount)) } func TestStatus(t *testing.T) { diff --git a/exporters/otlp/otlptrace/otlptracegrpc/client.go b/exporters/otlp/otlptrace/otlptracegrpc/client.go index d709ffa96e1..31ed8190b67 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/client.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/client.go @@ -273,3 +273,14 @@ func throttleDelay(status *status.Status) time.Duration { } return 0 } + +// MarshalLog is the marshaling function used by the logging system to represent this Client. +func (c *client) MarshalLog() interface{} { + return struct { + Type string + Endpoint string + }{ + Type: "otlphttpgrpc", + Endpoint: c.endpoint, + } +} diff --git a/exporters/otlp/otlptrace/otlptracegrpc/client_test.go b/exporters/otlp/otlptrace/otlptracegrpc/client_test.go index 9a02ca227b6..191ff23afd4 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/client_test.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/client_test.go @@ -374,7 +374,7 @@ func TestStartErrorInvalidAddress(t *testing.T) { } func TestEmptyData(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") + mc := runMockCollector(t) t.Cleanup(func() { require.NoError(t, mc.stop()) }) <-time.After(5 * time.Millisecond) diff --git a/exporters/otlp/otlptrace/otlptracegrpc/go.mod b/exporters/otlp/otlptrace/otlptracegrpc/go.mod index 7324309e2d9..024bd30407c 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/go.mod +++ b/exporters/otlp/otlptrace/otlptracegrpc/go.mod @@ -4,10 +4,10 @@ go 1.16 require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 go.opentelemetry.io/proto/otlp v0.12.0 go.uber.org/goleak v1.1.12 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 @@ -37,8 +37,6 @@ replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/openc 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 diff --git a/exporters/otlp/otlptrace/otlptracehttp/client.go b/exporters/otlp/otlptrace/otlptracehttp/client.go index 81487f9b6f9..9a1428b444c 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/client.go +++ b/exporters/otlp/otlptrace/otlptracehttp/client.go @@ -24,9 +24,7 @@ import ( "net" "net/http" "net/url" - "path" "strconv" - "strings" "sync" "time" @@ -79,26 +77,7 @@ var _ otlptrace.Client = (*client)(nil) // NewClient creates a new HTTP trace client. func NewClient(opts ...Option) otlptrace.Client { - cfg := otlpconfig.NewDefaultConfig() - cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg) - for _, opt := range opts { - cfg = 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 - } + cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(opts)...) httpClient := &http.Client{ Transport: ourTransport, @@ -243,6 +222,19 @@ func (d *client) newRequest(body []byte) (request, error) { return req, nil } +// MarshalLog is the marshaling function used by the logging system to represent this Client. +func (d *client) MarshalLog() interface{} { + return struct { + Type string + Endpoint string + Insecure bool + }{ + Type: "otlphttphttp", + Endpoint: d.cfg.Endpoint, + Insecure: d.cfg.Insecure, + } +} + // bodyReader returns a closure returning a new reader for buf. func bodyReader(buf []byte) func() io.ReadCloser { return func() io.ReadCloser { diff --git a/exporters/otlp/otlptrace/otlptracehttp/go.mod b/exporters/otlp/otlptrace/otlptracehttp/go.mod index 5c3486dc0c4..fb3ca1c3429 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/go.mod +++ b/exporters/otlp/otlptrace/otlptracehttp/go.mod @@ -4,11 +4,11 @@ go 1.16 require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 go.opentelemetry.io/proto/otlp v0.12.0 google.golang.org/protobuf v1.27.1 ) @@ -31,8 +31,6 @@ replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/o 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 diff --git a/exporters/otlp/otlptrace/otlptracehttp/options.go b/exporters/otlp/otlptrace/otlptracehttp/options.go index e550cfb5d51..4257ce3473f 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/options.go +++ b/exporters/otlp/otlptrace/otlptracehttp/options.go @@ -40,6 +40,14 @@ type Option interface { applyHTTPOption(otlpconfig.Config) otlpconfig.Config } +func asHTTPOptions(opts []Option) []otlpconfig.HTTPOption { + converted := make([]otlpconfig.HTTPOption, len(opts)) + for i, o := range opts { + converted[i] = otlpconfig.NewHTTPOption(o.applyHTTPOption) + } + return converted +} + // RetryConfig defines configuration for retrying batches in case of export // failure using an exponential backoff. type RetryConfig retry.Config @@ -55,7 +63,7 @@ func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config // 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 +// the default endpoint (localhost:4318). Note that the endpoint // must not contain any URL path. func WithEndpoint(endpoint string) Option { return wrappedOption{otlpconfig.WithEndpoint(endpoint)} diff --git a/exporters/prometheus/go.mod b/exporters/prometheus/go.mod index bfdbb4ab561..3d02af45d5f 100644 --- a/exporters/prometheus/go.mod +++ b/exporters/prometheus/go.mod @@ -5,10 +5,10 @@ go 1.16 require ( github.com/prometheus/client_golang v1.12.1 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 ) replace go.opentelemetry.io/otel => ../.. @@ -27,8 +27,6 @@ replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-co 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 diff --git a/exporters/prometheus/prometheus_test.go b/exporters/prometheus/prometheus_test.go index b05d9232117..587c8633e78 100644 --- a/exporters/prometheus/prometheus_test.go +++ b/exporters/prometheus/prometheus_test.go @@ -26,7 +26,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/prometheus" - "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/export/aggregation" @@ -107,9 +107,12 @@ func TestPrometheusExporter(t *testing.T) { 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") + upDownCounter, err := meter.SyncFloat64().UpDownCounter("updowncounter") + require.NoError(t, err) + counter, err := meter.SyncFloat64().Counter("counter") + require.NoError(t, err) + histogram, err := meter.SyncFloat64().Histogram("histogram") + require.NoError(t, err) labels := []attribute.KeyValue{ attribute.Key("A").String("B"), @@ -124,9 +127,13 @@ func TestPrometheusExporter(t *testing.T) { expected = append(expected, expectCounter("counter", `counter{A="B",C="D",R="V"} 15.3`)) - _ = metric.Must(meter).NewInt64GaugeObserver("intgaugeobserver", func(_ context.Context, result metric.Int64ObserverResult) { - result.Observe(1, labels...) + gaugeObserver, err := meter.AsyncInt64().Gauge("intgaugeobserver") + require.NoError(t, err) + + err = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) { + gaugeObserver.Observe(ctx, 1, labels...) }) + require.NoError(t, err) expected = append(expected, expectGauge("intgaugeobserver", `intgaugeobserver{A="B",C="D",R="V"} 1`)) @@ -148,15 +155,23 @@ func TestPrometheusExporter(t *testing.T) { expected = append(expected, expectGauge("updowncounter", `updowncounter{A="B",C="D",R="V"} 6.8`)) - _ = metric.Must(meter).NewFloat64CounterObserver("floatcounterobserver", func(_ context.Context, result metric.Float64ObserverResult) { - result.Observe(7.7, labels...) + counterObserver, err := meter.AsyncFloat64().Counter("floatcounterobserver") + require.NoError(t, err) + + err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) { + counterObserver.Observe(ctx, 7.7, labels...) }) + require.NoError(t, err) expected = append(expected, expectCounter("floatcounterobserver", `floatcounterobserver{A="B",C="D",R="V"} 7.7`)) - _ = metric.Must(meter).NewFloat64UpDownCounterObserver("floatupdowncounterobserver", func(_ context.Context, result metric.Float64ObserverResult) { - result.Observe(-7.7, labels...) + upDownCounterObserver, err := meter.AsyncFloat64().UpDownCounter("floatupdowncounterobserver") + require.NoError(t, err) + + err = meter.RegisterCallback([]instrument.Asynchronous{upDownCounterObserver}, func(ctx context.Context) { + upDownCounterObserver.Observe(ctx, -7.7, labels...) }) + require.NoError(t, err) expected = append(expected, expectGauge("floatupdowncounterobserver", `floatupdowncounterobserver{A="B",C="D",R="V"} -7.7`)) @@ -196,10 +211,8 @@ func TestPrometheusStatefulness(t *testing.T) { ctx := context.Background() - counter := metric.Must(meter).NewInt64Counter( - "a.counter", - metric.WithDescription("Counts things"), - ) + counter, err := meter.SyncInt64().Counter("a.counter", instrument.WithDescription("Counts things")) + require.NoError(t, err) counter.Add(ctx, 100, attribute.String("key", "value")) diff --git a/exporters/stdout/stdoutmetric/example_test.go b/exporters/stdout/stdoutmetric/example_test.go index c367b767040..1250a463a8c 100644 --- a/exporters/stdout/stdoutmetric/example_test.go +++ b/exporters/stdout/stdoutmetric/example_test.go @@ -21,7 +21,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/metric/instrument/syncint64" 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" @@ -33,13 +33,15 @@ const ( ) var ( - meter = global.GetMeterProvider().Meter( - instrumentationName, - metric.WithInstrumentationVersion(instrumentationVersion), - ) + // TODO Bring back Global package + // meter = global.GetMeterProvider().Meter( + // instrumentationName, + // metric.WithInstrumentationVersion(instrumentationVersion), + // ) + meter metric.Meter - loopCounter = metric.Must(meter).NewInt64Counter("function.loops") - paramValue = metric.Must(meter).NewInt64Histogram("function.param") + loopCounter syncint64.Counter + paramValue syncint64.Histogram nameKey = attribute.Key("function.name") ) @@ -80,7 +82,18 @@ func InstallExportPipeline(ctx context.Context) func() { if err = pusher.Start(ctx); err != nil { log.Fatalf("starting push controller: %v", err) } - global.SetMeterProvider(pusher) + // TODO Bring back Global package + // global.SetMeterProvider(pusher) + meter = pusher.Meter(instrumentationName, metric.WithInstrumentationVersion(instrumentationVersion)) + + loopCounter, err = meter.SyncInt64().Counter("function.loops") + if err != nil { + log.Fatalf("creating instrument: %v", err) + } + paramValue, err = meter.SyncInt64().Histogram("function.param") + if err != nil { + log.Fatalf("creating instrument: %v", err) + } return func() { if err := pusher.Stop(ctx); err != nil { @@ -92,7 +105,7 @@ func InstallExportPipeline(ctx context.Context) func() { func Example() { ctx := context.Background() - // Registers a meter Provider globally. + // TODO: Registers a meter Provider globally. cleanup := InstallExportPipeline(ctx) defer cleanup() diff --git a/exporters/stdout/stdoutmetric/go.mod b/exporters/stdout/stdoutmetric/go.mod index c438a6f0037..07989e72e67 100644 --- a/exporters/stdout/stdoutmetric/go.mod +++ b/exporters/stdout/stdoutmetric/go.mod @@ -9,10 +9,10 @@ replace ( require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/sdk/metric v0.26.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus @@ -27,8 +27,6 @@ replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencens 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 diff --git a/exporters/stdout/stdoutmetric/metric_test.go b/exporters/stdout/stdoutmetric/metric_test.go index e9153fcfd0a..33e2831dbd7 100644 --- a/exporters/stdout/stdoutmetric/metric_test.go +++ b/exporters/stdout/stdoutmetric/metric_test.go @@ -101,7 +101,8 @@ func TestStdoutTimestamp(t *testing.T) { require.NoError(t, cont.Start(ctx)) meter := cont.Meter("test") - counter := metric.Must(meter).NewInt64Counter("name.lastvalue") + counter, err := meter.SyncInt64().Counter("name.lastvalue") + require.NoError(t, err) before := time.Now() // Ensure the timestamp is after before. @@ -137,7 +138,8 @@ func TestStdoutTimestamp(t *testing.T) { func TestStdoutCounterFormat(t *testing.T) { fix := newFixture(t) - counter := metric.Must(fix.meter).NewInt64Counter("name.sum") + counter, err := fix.meter.SyncInt64().Counter("name.sum") + require.NoError(t, err) counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D")) require.NoError(t, fix.cont.Stop(fix.ctx)) @@ -148,7 +150,8 @@ func TestStdoutCounterFormat(t *testing.T) { func TestStdoutLastValueFormat(t *testing.T) { fix := newFixture(t) - counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") + counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue") + require.NoError(t, err) counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D")) require.NoError(t, fix.cont.Stop(fix.ctx)) @@ -159,7 +162,8 @@ func TestStdoutLastValueFormat(t *testing.T) { func TestStdoutHistogramFormat(t *testing.T) { fix := newFixture(t, stdoutmetric.WithPrettyPrint()) - inst := metric.Must(fix.meter).NewFloat64Histogram("name.histogram") + inst, err := fix.meter.SyncFloat64().Histogram("name.histogram") + require.NoError(t, err) for i := 0; i < 1000; i++ { inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D")) @@ -181,7 +185,8 @@ func TestStdoutNoData(t *testing.T) { t.Parallel() fix := newFixture(t) - _ = metric.Must(fix.meter).NewFloat64Counter(fmt.Sprint("name.", aggName)) + _, err := fix.meter.SyncFloat64().Counter(fmt.Sprint("name.", aggName)) + require.NoError(t, err) require.NoError(t, fix.cont.Stop(fix.ctx)) require.Equal(t, "", fix.Output()) @@ -243,7 +248,8 @@ func TestStdoutResource(t *testing.T) { ctx := context.Background() fix := newFixtureWithResource(t, tc.res) - counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") + counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue") + require.NoError(t, err) counter.Add(ctx, 123.456, tc.attrs...) require.NoError(t, fix.cont.Stop(fix.ctx)) diff --git a/exporters/stdout/stdouttrace/go.mod b/exporters/stdout/stdouttrace/go.mod index 9407448a61a..88d59d2d605 100644 --- a/exporters/stdout/stdouttrace/go.mod +++ b/exporters/stdout/stdouttrace/go.mod @@ -9,9 +9,9 @@ replace ( require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../../bridge/opencensus @@ -26,8 +26,6 @@ replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencens 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 diff --git a/exporters/zipkin/go.mod b/exporters/zipkin/go.mod index e517881375e..c9d56ed8d03 100644 --- a/exporters/zipkin/go.mod +++ b/exporters/zipkin/go.mod @@ -6,9 +6,9 @@ require ( github.com/google/go-cmp v0.5.7 github.com/openzipkin/zipkin-go v0.4.0 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/sdk v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus @@ -23,8 +23,6 @@ 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 diff --git a/exporters/zipkin/model.go b/exporters/zipkin/model.go index ba9b0f00987..38a7368b7a5 100644 --- a/exporters/zipkin/model.go +++ b/exporters/zipkin/model.go @@ -168,6 +168,27 @@ func attributesToJSONMapString(attributes []attribute.KeyValue) string { return (string)(jsonBytes) } +// attributeToStringPair serializes each attribute to a string pair +func attributeToStringPair(kv attribute.KeyValue) (string, string) { + switch kv.Value.Type() { + // For slice attributes, serialize as JSON list string. + case attribute.BOOLSLICE: + json, _ := json.Marshal(kv.Value.AsBoolSlice()) + return (string)(kv.Key), (string)(json) + case attribute.INT64SLICE: + json, _ := json.Marshal(kv.Value.AsInt64Slice()) + return (string)(kv.Key), (string)(json) + case attribute.FLOAT64SLICE: + json, _ := json.Marshal(kv.Value.AsFloat64Slice()) + return (string)(kv.Key), (string)(json) + case attribute.STRINGSLICE: + json, _ := json.Marshal(kv.Value.AsStringSlice()) + return (string)(kv.Key), (string)(json) + default: + return (string)(kv.Key), kv.Value.Emit() + } +} + // extraZipkinTags are those that may be added to every outgoing span var extraZipkinTags = []string{ "otel.status_code", @@ -177,25 +198,15 @@ var extraZipkinTags = []string{ func toZipkinTags(data tracesdk.ReadOnlySpan) map[string]string { attr := data.Attributes() - m := make(map[string]string, len(attr)+len(extraZipkinTags)) + resourceAttr := data.Resource().Attributes() + m := make(map[string]string, len(attr)+len(resourceAttr)+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() - } + k, v := attributeToStringPair(kv) + m[k] = v + } + for _, kv := range resourceAttr { + k, v := attributeToStringPair(kv) + m[k] = v } if data.Status().Code != codes.Unset { diff --git a/exporters/zipkin/model_test.go b/exporters/zipkin/model_test.go index d4ca9c43ec0..09c6f704418 100644 --- a/exporters/zipkin/model_test.go +++ b/exporters/zipkin/model_test.go @@ -39,6 +39,9 @@ import ( func TestModelConversion(t *testing.T) { resource := resource.NewSchemaless( semconv.ServiceNameKey.String("model-test"), + semconv.ServiceVersionKey.String("0.1.0"), + attribute.Int64("resource-attr1", 42), + attribute.IntSlice("resource-attr2", []int{0, 1, 2}), ) inputBatch := tracetest.SpanStubs{ @@ -408,6 +411,10 @@ func TestModelConversion(t *testing.T) { "attr3": "[0,1,2]", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data with no parent @@ -447,6 +454,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data of unspecified kind @@ -486,6 +497,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data of internal kind @@ -525,6 +540,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data of client kind @@ -570,6 +589,10 @@ func TestModelConversion(t *testing.T) { "peer.hostname": "test-peer-hostname", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data of producer kind @@ -609,6 +632,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data of consumer kind @@ -648,6 +675,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data with no events @@ -678,6 +709,10 @@ func TestModelConversion(t *testing.T) { "attr2": "bar", "otel.status_code": "Error", "error": "404, file not found", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", }, }, // model for span data with an "error" attribute set to "false" @@ -712,7 +747,12 @@ func TestModelConversion(t *testing.T) { Value: "ev2", }, }, - Tags: nil, // should be omitted + Tags: map[string]string{ + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", + }, // only resource tags should be included }, } gottenOutputBatch := SpanModels(inputBatch) diff --git a/exporters/zipkin/zipkin.go b/exporters/zipkin/zipkin.go index 5c7c20049d9..be14ff12879 100644 --- a/exporters/zipkin/zipkin.go +++ b/exporters/zipkin/zipkin.go @@ -180,3 +180,14 @@ func (e *Exporter) errf(format string, args ...interface{}) error { e.logf(format, args...) return fmt.Errorf(format, args...) } + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (e *Exporter) MarshalLog() interface{} { + return struct { + Type string + URL string + }{ + Type: "zipkin", + URL: e.url, + } +} diff --git a/exporters/zipkin/zipkin_test.go b/exporters/zipkin/zipkin_test.go index c6984e572e1..c3abe353a37 100644 --- a/exporters/zipkin/zipkin_test.go +++ b/exporters/zipkin/zipkin_test.go @@ -200,6 +200,7 @@ func logStoreLogger(s *logStore) *log.Logger { func TestExportSpans(t *testing.T) { resource := resource.NewSchemaless( semconv.ServiceNameKey.String("exporter-test"), + semconv.ServiceVersionKey.String("0.1.0"), ) spans := tracetest.SpanStubs{ @@ -271,6 +272,8 @@ func TestExportSpans(t *testing.T) { Tags: map[string]string{ "otel.status_code": "Error", "error": "404, file not found", + "service.name": "exporter-test", + "service.version": "0.1.0", }, }, // model of child @@ -299,6 +302,8 @@ func TestExportSpans(t *testing.T) { Tags: map[string]string{ "otel.status_code": "Error", "error": "403, forbidden", + "service.name": "exporter-test", + "service.version": "0.1.0", }, }, } diff --git a/go.mod b/go.mod index 0469e2fa55d..f37c9bf371a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/go-logr/stdr v1.2.2 github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel/trace v1.4.1 ) replace go.opentelemetry.io/otel => ./ @@ -24,8 +24,6 @@ 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 diff --git a/internal/tools/common.go b/internal/tools/common.go index 525e5adcf3b..0c7389934ca 100644 --- a/internal/tools/common.go +++ b/internal/tools/common.go @@ -18,18 +18,28 @@ package tools // import "go.opentelemetry.io/otel/internal/tools" import ( + "bytes" "errors" "fmt" + "io" "os" "path/filepath" + "sort" "strings" + "text/tabwriter" + + "golang.org/x/mod/modfile" ) -// 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) { +// Repo represents a git repository. +type Repo string + +// 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() (Repo, error) { start, err := os.Getwd() if err != nil { return "", err @@ -52,6 +62,63 @@ func FindRepoRoot() (string, error) { return "", err } - return dir, nil + return Repo(dir), nil + } +} + +// FindModules returns all Go modules contained in Repo r. +func (r Repo) FindModules() ([]*modfile.File, error) { + var results []*modfile.File + err := filepath.Walk(string(r), func(path string, info os.FileInfo, walkErr error) error { + if walkErr != nil { + // Walk failed to walk into this directory. Stop walking and + // signal this error. + return walkErr + } + + if !info.IsDir() { + return nil + } + + goMod := filepath.Join(path, "go.mod") + f, err := os.Open(goMod) + if errors.Is(err, os.ErrNotExist) { + return nil + } + if err != nil { + return err + } + + var b bytes.Buffer + io.Copy(&b, f) + if err = f.Close(); err != nil { + return err + } + + mFile, err := modfile.Parse(goMod, b.Bytes(), nil) + if err != nil { + return err + } + results = append(results, mFile) + return nil + }) + + sort.SliceStable(results, func(i, j int) bool { + return results[i].Syntax.Name < results[j].Syntax.Name + }) + + return results, err +} + +func PrintModFiles(w io.Writer, mFiles []*modfile.File) 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 mFiles { + if _, err := fmt.Fprintf(tw, "%s\t%s\n", m.Syntax.Name, m.Module.Mod.Path); err != nil { + return err + } } + return tw.Flush() } diff --git a/internal/tools/crosslink/crosslink.go b/internal/tools/crosslink/crosslink.go index a229bfa6db0..5f19d5b946e 100644 --- a/internal/tools/crosslink/crosslink.go +++ b/internal/tools/crosslink/crosslink.go @@ -25,106 +25,42 @@ package main import ( - "encoding/json" - "errors" - "fmt" - "io" "log" "os" - "os/exec" "path/filepath" "strings" - "text/tabwriter" "go.opentelemetry.io/otel/internal/tools" + "golang.org/x/mod/modfile" ) -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 { +func crossLink(m []*modfile.File) error { for _, from := range m { - args := []string{"mod", "edit"} - + basepath := filepath.Dir(from.Syntax.Name) for _, to := range m { - localPath, err := filepath.Rel(from.filePath, to.filePath) + newPath, err := filepath.Rel(basepath, filepath.Dir(to.Syntax.Name)) if err != nil { return err } - if localPath == "." || localPath == ".." { - localPath += "/" - } else if !strings.HasPrefix(localPath, "..") { - localPath = "./" + localPath + switch { + case newPath == ".", newPath == "..": + newPath += "/" + case !strings.HasPrefix(newPath, ".."): + newPath = "./" + newPath } - args = append(args, "-replace", to.importPath+"="+localPath) + from.AddReplace(to.Module.Mod.Path, "", newPath, "") } - cmd := exec.Command("go", args...) - cmd.Dir = from.filePath - out, err := cmd.CombinedOutput() + from.Cleanup() + + f, err := os.OpenFile(from.Syntax.Name, os.O_RDWR|os.O_TRUNC, 0755) if err != nil { - log.Println(string(out)) + return err + } + if _, err = f.Write(modfile.Format(from.Syntax)); err != nil { + return err + } + if err = f.Close(); err != nil { return err } } @@ -132,23 +68,21 @@ func (m mods) crossLink() error { } func main() { - repoRootStr, err := tools.FindRepoRoot() + root, err := tools.FindRepoRoot() if err != nil { log.Fatalf("unable to find repo root: %v", err) } - repoRoot := repo(repoRootStr) - - mods, err := repoRoot.findModules() + mods, err := root.FindModules() if err != nil { log.Fatalf("unable to list modules: %v", err) } - if err := mods.print(os.Stdout); err != nil { + if err := tools.PrintModFiles(os.Stdout, mods); err != nil { log.Fatalf("unable to print modules: %v", err) } - if err := mods.crossLink(); err != nil { + if err := crossLink(mods); err != nil { log.Fatalf("unable to crosslink: %v", err) } } diff --git a/internal/tools/dbotconf/dbotconf.go b/internal/tools/dbotconf/dbotconf.go new file mode 100644 index 00000000000..a81a3a83fb9 --- /dev/null +++ b/internal/tools/dbotconf/dbotconf.go @@ -0,0 +1,112 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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 provides a utility to generate a complete dependabot +// configuration for a repository with multiple Go modules. +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + "sort" + "strings" + "text/template" + + "go.opentelemetry.io/otel/internal/tools" + "golang.org/x/mod/modfile" +) + +var configPtr = flag.String("config", "./.github/dependabot.yml", "dependabot configuration path") + +const configTemplate = `# File generated by "make dependabot-generate"; DO NOT EDIT. + +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + labels: + - dependencies + - actions + - "Skip Changelog" + schedule: + day: sunday + interval: weekly +{{- range .}} + - package-ecosystem: gomod + directory: {{.}} + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + day: sunday + interval: weekly +{{- end}} +` + +func gomodDirectories(basePath string, mods []*modfile.File) []string { + var dirs []string + for _, m := range mods { + targetPath := filepath.Dir(m.Syntax.Name) + relPath := strings.TrimPrefix(targetPath, basePath) + if relPath == "" { + relPath = "/" + } + dirs = append(dirs, relPath) + } + sort.Strings(dirs) + return dirs +} + +func generate(path string) error { + tpl, err := template.New("dependabot.yml").Parse(configTemplate) + if err != nil { + return fmt.Errorf("parse template: %w", err) + } + + root, err := tools.FindRepoRoot() + if err != nil { + return fmt.Errorf("find repo root: %w", err) + } + + mods, err := root.FindModules() + if err != nil { + return fmt.Errorf("list modules: %w", err) + } + data := gomodDirectories(string(root), mods) + + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + if err = tpl.Execute(f, data); err != nil { + // Best effort. + _ = f.Close() + return fmt.Errorf("rendering template: %w", err) + } + if err = f.Close(); err != nil { + return fmt.Errorf("closing %s: %w", path, err) + } + return nil +} + +func main() { + flag.Parse() + if err := generate(*configPtr); err != nil { + log.Fatalf("failed to generate dependabot configuration: %v", err) + } +} diff --git a/internal/tools/go.mod b/internal/tools/go.mod index fbaac2e35a9..583d72b1a50 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -5,12 +5,13 @@ go 1.16 require ( github.com/client9/misspell v0.3.4 github.com/gogo/protobuf v1.3.2 - github.com/golangci/golangci-lint v1.44.0 + github.com/golangci/golangci-lint v1.44.2 github.com/itchyny/gojq v0.12.6 github.com/jcchavezs/porto v0.4.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 golang.org/x/tools v0.1.9 ) diff --git a/internal/tools/go.sum b/internal/tools/go.sum index ae43fe3288b..1604fe5e896 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -118,14 +118,14 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB 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/blizzy78/varnamelen v0.5.0 h1:v9LpMwxzTqAJC4lsD/jR7zWb8a66trcqhTEH4Mk6Fio= -github.com/blizzy78/varnamelen v0.5.0/go.mod h1:Mc0nLBKI1/FP0Ga4kqMOgBig0eS5QtR107JnMAb1Wuc= +github.com/blizzy78/varnamelen v0.6.0 h1:TOIDk9qRIMspALZKX8x+5hQfAjuvAFogppnxtvuNmBo= +github.com/blizzy78/varnamelen v0.6.0/go.mod h1:zy2Eic4qWqjrxa60jG34cfL0VXcSwzUrIx68eJPb4Q8= 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/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= -github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= -github.com/breml/errchkjson v0.2.1 h1:QCToXnY9BNngrbJoW3qfCTt3BdtbnsI6wyP/WGrxxSE= -github.com/breml/errchkjson v0.2.1/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= +github.com/breml/bidichk v0.2.2 h1:w7QXnpH0eCBJm55zGCTJveZEkQBt6Fs5zThIdA6qQ9Y= +github.com/breml/bidichk v0.2.2/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= +github.com/breml/errchkjson v0.2.3 h1:97eGTmR/w0paL2SwfRPI1jaAZHaH/fXnxWTw2eEIqE0= +github.com/breml/errchkjson v0.2.3/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -172,8 +172,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/daixiang0/gci v0.3.1-0.20220208004058-76d765e3ab48 h1:9rJGqaC5do9zkvKrtRdx0HJoxj7Jd6vDa0O2eBU0AbU= +github.com/daixiang0/gci v0.3.1-0.20220208004058-76d765e3ab48/go.mod h1:jaASoJmv/ykO9dAAPy31iJnreV19248qKDdVWf3QgC4= 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= @@ -318,8 +318,8 @@ github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZB 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.44.0 h1:YJPouGNQEdK+x2KsCpWMIBy0q6MSuxHjkWMxJMNj/DU= -github.com/golangci/golangci-lint v1.44.0/go.mod h1:aBolpzNkmYogKPynGKdOWDCEc8LlwnxZC6w/SJ1TaEs= +github.com/golangci/golangci-lint v1.44.2 h1:MzvkDt1j1OHkv42/feNJVNNXRFACPp7aAWBWDo5aYQw= +github.com/golangci/golangci-lint v1.44.2/go.mod h1:KjBgkLvsTWDkhfu12iCrv0gwL1kON5KNhbyjQ6qN7Jo= 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= @@ -345,8 +345,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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/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/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/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= @@ -457,6 +458,8 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 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= @@ -531,8 +534,8 @@ 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.5.0 h1:CiEKStgoG4K9bjf/zk3eNX0D0J2iFWzxEY+h9UXmlJg= -github.com/kulti/thelper v0.5.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= +github.com/kulti/thelper v0.5.1 h1:Uf4CUekH0OvzQTFPrWkstJvXgm6pnNEtQu3HiqEkpB0= +github.com/kulti/thelper v0.5.1/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= github.com/kunwardeep/paralleltest v1.0.3 h1:UdKIkImEAXjR1chUWLn+PNXqWUGs//7tzMeWuP7NhmI= github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= @@ -540,8 +543,8 @@ github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77 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.3.0 h1:Aubm2ZsrsjIGFvdxemMPJaXrSJ5Cys6VWyTQFt9k2dI= -github.com/ldez/tagliatelle v0.3.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= +github.com/ldez/tagliatelle v0.3.1 h1:3BqVVlReVUZwafJUwQ+oxbx2BEX2vUG4Yu/NOfMiKiM= +github.com/ldez/tagliatelle v0.3.1/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg= github.com/leonklingele/grouper v1.1.0/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= @@ -589,8 +592,8 @@ github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwg github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= -github.com/mgechev/revive v1.1.3 h1:6tBZacs2/uv9UOpkBQhCtXh2NGgu2Ry97ZyjcN6uDCM= -github.com/mgechev/revive v1.1.3/go.mod h1:jMzDa13teAuv/KLeqgJw79NDe+1IT0ZO3Mht0vN1Yls= +github.com/mgechev/revive v1.1.4 h1:sZOjY6GU35Kr9jKa/wsKSHgrFz8eASIB5i3tqWZMp0A= +github.com/mgechev/revive v1.1.4/go.mod h1:ZZq2bmyssGh8MSPz3VVziqRNIMYTJXzP8MUKG90vZ9A= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -714,7 +717,7 @@ github.com/quasilyte/go-ruleguard v0.3.15/go.mod h1:NhuWhnlVEM1gT1A4VJHYfy9MuYSx github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.12/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/dsl v0.3.15/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.17/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-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3 h1:P4QPNn+TK49zJjXKERt/vyPbv/mCHB/zQ4flDYOMN+M= @@ -748,7 +751,7 @@ 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.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA= +github.com/shirou/gopsutil/v3 v3.22.1/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= 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= @@ -839,8 +842,8 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri 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/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= +github.com/ultraware/whitespace v0.0.5/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= @@ -1052,6 +1055,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ 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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 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= @@ -1135,15 +1139,15 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/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= @@ -1243,7 +1247,6 @@ golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4f 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= diff --git a/metric/config.go b/metric/config.go index 6049015cbe7..621e4c5fcb8 100644 --- a/metric/config.go +++ b/metric/config.go @@ -32,7 +32,7 @@ func (cfg MeterConfig) SchemaURL() string { // MeterOption is an interface for applying Meter options. type MeterOption interface { - // ApplyMeter is used to set a MeterOption value of a MeterConfig. + // applyMeter is used to set a MeterOption value of a MeterConfig. applyMeter(MeterConfig) MeterConfig } diff --git a/metric/example_test.go b/metric/example_test.go index 573d7070dd4..92e0e2cd958 100644 --- a/metric/example_test.go +++ b/metric/example_test.go @@ -20,15 +20,16 @@ import ( "runtime" "time" - "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/instrument/syncfloat64" + "go.opentelemetry.io/otel/metric/nonrecording" "go.opentelemetry.io/otel/metric/unit" ) -func ExampleSyncInstrument() { +//nolint:govet // Meter doesn't register for go vet +func ExampleMeter_synchronous() { // In a library or program this would be provided by otel.GetMeterProvider(). - meterProvider := metric.NewNoopMeterProvider() + meterProvider := nonrecording.NewNoopMeterProvider() workDuration, err := meterProvider.Meter("go.opentelemetry.io/otel/metric#SyncExample").SyncInt64().Histogram( "workDuration", @@ -42,13 +43,14 @@ func ExampleSyncInstrument() { ctx := context.Background() // Do work // ... - workDuration.Record(ctx, time.Now().Sub(startTime).Milliseconds()) + workDuration.Record(ctx, time.Since(startTime).Milliseconds()) } -func ExampleAsyncInstrument() { +//nolint:govet // Meter doesn't register for go vet +func ExampleMeter_asynchronous_single() { // In a library or program this would be provided by otel.GetMeterProvider(). - meterProvider := metric.NewNoopMeterProvider() + meterProvider := nonrecording.NewNoopMeterProvider() meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#AsyncExample") memoryUsage, err := meter.AsyncInt64().Gauge( @@ -75,8 +77,9 @@ func ExampleAsyncInstrument() { } } -func ExampleMultipleAsyncInstrument() { - meterProvider := metric.NewNoopMeterProvider() +//nolint:govet // Meter doesn't register for go vet +func ExampleMeter_asynchronous_multiple() { + meterProvider := nonrecording.NewNoopMeterProvider() meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#MultiAsyncExample") // This is just a sample of memory stats to record from the Memstats diff --git a/metric/go.mod b/metric/go.mod index 013bdad17aa..7d510be1f32 100644 --- a/metric/go.mod +++ b/metric/go.mod @@ -2,7 +2,7 @@ module go.opentelemetry.io/otel/metric go 1.16 -require go.opentelemetry.io/otel v1.3.0 +require go.opentelemetry.io/otel v1.4.1 replace go.opentelemetry.io/otel => ../ @@ -18,8 +18,6 @@ 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 diff --git a/metric/instrument/asyncfloat64/asyncfloat64.go b/metric/instrument/asyncfloat64/asyncfloat64.go index aae69b138c5..91c034fb2a0 100644 --- a/metric/instrument/asyncfloat64/asyncfloat64.go +++ b/metric/instrument/asyncfloat64/asyncfloat64.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/metric/instrument" ) -// Instruments provides access to individual instruments. -type Instruments interface { +// InstrumentProvider provides access to individual instruments. +type InstrumentProvider interface { // Counter creates an instrument for recording increasing values. Counter(name string, opts ...instrument.Option) (Counter, error) diff --git a/metric/instrument/asyncfloat64/noop.go b/metric/instrument/asyncfloat64/noop.go deleted file mode 100644 index 2b6ce755085..00000000000 --- a/metric/instrument/asyncfloat64/noop.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package asyncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - -import ( - "context" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument" -) - -func NewNoopInstruments() Instruments { - return nonrecordingInstrument{} -} - -type nonrecordingInstrument struct { - instrument.Asynchronous -} - -var ( - _ Instruments = nonrecordingInstrument{} - _ Counter = nonrecordingInstrument{} - _ UpDownCounter = nonrecordingInstrument{} - _ Gauge = nonrecordingInstrument{} -) - -func (n nonrecordingInstrument) Counter(name string, opts ...instrument.Option) (Counter, error) { - return n, nil -} - -func (n nonrecordingInstrument) UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error) { - return n, nil -} - -func (n nonrecordingInstrument) Gauge(name string, opts ...instrument.Option) (Gauge, error) { - return n, nil -} - -func (nonrecordingInstrument) Observe(context.Context, float64, ...attribute.KeyValue) { - -} diff --git a/metric/instrument/asyncint64/asyncint64.go b/metric/instrument/asyncint64/asyncint64.go index ad3668e2183..9dfba553d78 100644 --- a/metric/instrument/asyncint64/asyncint64.go +++ b/metric/instrument/asyncint64/asyncint64.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/metric/instrument" ) -// Instruments provides access to individual instruments. -type Instruments interface { +// InstrumentProvider provides access to individual instruments. +type InstrumentProvider interface { // Counter creates an instrument for recording increasing values. Counter(name string, opts ...instrument.Option) (Counter, error) diff --git a/metric/instrument/asyncint64/noop.go b/metric/instrument/asyncint64/noop.go deleted file mode 100644 index d55bf48fd83..00000000000 --- a/metric/instrument/asyncint64/noop.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package asyncint64 // import "go.opentelemetry.io/otel/metric/instrument/asyncint64" - -import ( - "context" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument" -) - -func NewNoopInstruments() Instruments { - return nonrecordingInstrument{} -} - -type nonrecordingInstrument struct { - instrument.Asynchronous -} - -var ( - _ Instruments = nonrecordingInstrument{} - _ Counter = nonrecordingInstrument{} - _ UpDownCounter = nonrecordingInstrument{} - _ Gauge = nonrecordingInstrument{} -) - -func (n nonrecordingInstrument) Counter(name string, opts ...instrument.Option) (Counter, error) { - return n, nil -} - -func (n nonrecordingInstrument) UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error) { - return n, nil -} - -func (n nonrecordingInstrument) Gauge(name string, opts ...instrument.Option) (Gauge, error) { - return n, nil -} - -func (nonrecordingInstrument) Observe(context.Context, int64, ...attribute.KeyValue) { -} diff --git a/metric/instrument/syncfloat64/noop.go b/metric/instrument/syncfloat64/noop.go deleted file mode 100644 index 2e7cfe56af9..00000000000 --- a/metric/instrument/syncfloat64/noop.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package syncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - -import ( - "context" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument" -) - -func NewNoopInstruments() Instruments { - return nonrecordingInstrument{} -} - -type nonrecordingInstrument struct { - instrument.Synchronous -} - -var ( - _ Instruments = nonrecordingInstrument{} - _ Counter = nonrecordingInstrument{} - _ UpDownCounter = nonrecordingInstrument{} - _ Histogram = nonrecordingInstrument{} -) - -func (n nonrecordingInstrument) Counter(name string, opts ...instrument.Option) (Counter, error) { - return n, nil -} - -func (n nonrecordingInstrument) UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error) { - return n, nil -} - -func (n nonrecordingInstrument) Histogram(name string, opts ...instrument.Option) (Histogram, error) { - return n, nil -} - -func (nonrecordingInstrument) Add(context.Context, float64, ...attribute.KeyValue) { - -} - -func (nonrecordingInstrument) Record(context.Context, float64, ...attribute.KeyValue) { - -} diff --git a/metric/instrument/syncfloat64/syncfloat64.go b/metric/instrument/syncfloat64/syncfloat64.go index cd3d721535b..1989292ecf8 100644 --- a/metric/instrument/syncfloat64/syncfloat64.go +++ b/metric/instrument/syncfloat64/syncfloat64.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/metric/instrument" ) -// Instruments provides access to individual instruments. -type Instruments interface { +// InstrumentProvider provides access to individual instruments. +type InstrumentProvider interface { // Counter creates an instrument for recording increasing values. Counter(name string, opts ...instrument.Option) (Counter, error) // UpDownCounter creates an instrument for recording changes of a value. diff --git a/metric/instrument/syncint64/noop.go b/metric/instrument/syncint64/noop.go deleted file mode 100644 index fc5d51c4f52..00000000000 --- a/metric/instrument/syncint64/noop.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package syncint64 // import "go.opentelemetry.io/otel/metric/instrument/syncint64" - -import ( - "context" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument" -) - -func NewNoopInstruments() Instruments { - return nonrecordingInstrument{} -} - -type nonrecordingInstrument struct { - instrument.Synchronous -} - -var ( - _ Instruments = nonrecordingInstrument{} - _ Counter = nonrecordingInstrument{} - _ UpDownCounter = nonrecordingInstrument{} - _ Histogram = nonrecordingInstrument{} -) - -func (n nonrecordingInstrument) Counter(name string, opts ...instrument.Option) (Counter, error) { - return n, nil -} - -func (n nonrecordingInstrument) UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error) { - return n, nil -} - -func (n nonrecordingInstrument) Histogram(name string, opts ...instrument.Option) (Histogram, error) { - return n, nil -} - -func (nonrecordingInstrument) Add(context.Context, int64, ...attribute.KeyValue) { -} -func (nonrecordingInstrument) Record(context.Context, int64, ...attribute.KeyValue) { -} diff --git a/metric/instrument/syncint64/syncint64.go b/metric/instrument/syncint64/syncint64.go index 96f99749b49..ee882bcd922 100644 --- a/metric/instrument/syncint64/syncint64.go +++ b/metric/instrument/syncint64/syncint64.go @@ -21,8 +21,8 @@ import ( "go.opentelemetry.io/otel/metric/instrument" ) -// Instruments provides access to individual instruments. -type Instruments interface { +// InstrumentProvider provides access to individual instruments. +type InstrumentProvider interface { // Counter creates an instrument for recording increasing values. Counter(name string, opts ...instrument.Option) (Counter, error) // UpDownCounter creates an instrument for recording changes of a value. diff --git a/metric/meter.go b/metric/meter.go index b79e6433db1..21fc1c499fb 100644 --- a/metric/meter.go +++ b/metric/meter.go @@ -40,12 +40,12 @@ type Meter interface { // AsyncInt64 is the namespace for the Asynchronous Integer instruments. // // To Observe data with instruments it must be registered in a callback. - AsyncInt64() asyncint64.Instruments + AsyncInt64() asyncint64.InstrumentProvider // AsyncFloat64 is the namespace for the Asynchronous Float instruments // // To Observe data with instruments it must be registered in a callback. - AsyncFloat64() asyncfloat64.Instruments + AsyncFloat64() asyncfloat64.InstrumentProvider // RegisterCallback captures the function that will be called during Collect. // @@ -54,7 +54,7 @@ type Meter interface { RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error // SyncInt64 is the namespace for the Synchronous Integer instruments - SyncInt64() syncint64.Instruments + SyncInt64() syncint64.InstrumentProvider // SyncFloat64 is the namespace for the Synchronous Float instruments - SyncFloat64() syncfloat64.Instruments + SyncFloat64() syncfloat64.InstrumentProvider } diff --git a/metric/nonrecording/instruments.go b/metric/nonrecording/instruments.go new file mode 100644 index 00000000000..84a6aa89c31 --- /dev/null +++ b/metric/nonrecording/instruments.go @@ -0,0 +1,138 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording" + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric/instrument" + "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" + "go.opentelemetry.io/otel/metric/instrument/asyncint64" + "go.opentelemetry.io/otel/metric/instrument/syncfloat64" + "go.opentelemetry.io/otel/metric/instrument/syncint64" +) + +type nonrecordingAsyncFloat64Instrument struct { + instrument.Asynchronous +} + +var ( + _ asyncfloat64.InstrumentProvider = nonrecordingAsyncFloat64Instrument{} + _ asyncfloat64.Counter = nonrecordingAsyncFloat64Instrument{} + _ asyncfloat64.UpDownCounter = nonrecordingAsyncFloat64Instrument{} + _ asyncfloat64.Gauge = nonrecordingAsyncFloat64Instrument{} +) + +func (n nonrecordingAsyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) { + return n, nil +} + +func (n nonrecordingAsyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) { + return n, nil +} + +func (n nonrecordingAsyncFloat64Instrument) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) { + return n, nil +} + +func (nonrecordingAsyncFloat64Instrument) Observe(context.Context, float64, ...attribute.KeyValue) { + +} + +type nonrecordingAsyncInt64Instrument struct { + instrument.Asynchronous +} + +var ( + _ asyncint64.InstrumentProvider = nonrecordingAsyncInt64Instrument{} + _ asyncint64.Counter = nonrecordingAsyncInt64Instrument{} + _ asyncint64.UpDownCounter = nonrecordingAsyncInt64Instrument{} + _ asyncint64.Gauge = nonrecordingAsyncInt64Instrument{} +) + +func (n nonrecordingAsyncInt64Instrument) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) { + return n, nil +} + +func (n nonrecordingAsyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) { + return n, nil +} + +func (n nonrecordingAsyncInt64Instrument) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) { + return n, nil +} + +func (nonrecordingAsyncInt64Instrument) Observe(context.Context, int64, ...attribute.KeyValue) { +} + +type nonrecordingSyncFloat64Instrument struct { + instrument.Synchronous +} + +var ( + _ syncfloat64.InstrumentProvider = nonrecordingSyncFloat64Instrument{} + _ syncfloat64.Counter = nonrecordingSyncFloat64Instrument{} + _ syncfloat64.UpDownCounter = nonrecordingSyncFloat64Instrument{} + _ syncfloat64.Histogram = nonrecordingSyncFloat64Instrument{} +) + +func (n nonrecordingSyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) { + return n, nil +} + +func (n nonrecordingSyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) { + return n, nil +} + +func (n nonrecordingSyncFloat64Instrument) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) { + return n, nil +} + +func (nonrecordingSyncFloat64Instrument) Add(context.Context, float64, ...attribute.KeyValue) { + +} + +func (nonrecordingSyncFloat64Instrument) Record(context.Context, float64, ...attribute.KeyValue) { + +} + +type nonrecordingSyncInt64Instrument struct { + instrument.Synchronous +} + +var ( + _ syncint64.InstrumentProvider = nonrecordingSyncInt64Instrument{} + _ syncint64.Counter = nonrecordingSyncInt64Instrument{} + _ syncint64.UpDownCounter = nonrecordingSyncInt64Instrument{} + _ syncint64.Histogram = nonrecordingSyncInt64Instrument{} +) + +func (n nonrecordingSyncInt64Instrument) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) { + return n, nil +} + +func (n nonrecordingSyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) { + return n, nil +} + +func (n nonrecordingSyncInt64Instrument) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) { + return n, nil +} + +func (nonrecordingSyncInt64Instrument) Add(context.Context, int64, ...attribute.KeyValue) { +} +func (nonrecordingSyncInt64Instrument) Record(context.Context, int64, ...attribute.KeyValue) { +} diff --git a/metric/noop.go b/metric/nonrecording/meter.go similarity index 56% rename from metric/noop.go rename to metric/nonrecording/meter.go index 975c0236012..2acea49d8a1 100644 --- a/metric/noop.go +++ b/metric/nonrecording/meter.go @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metric // import "go.opentelemetry.io/otel/metric" +package nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording" import ( "context" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" "go.opentelemetry.io/otel/metric/instrument/asyncint64" @@ -24,33 +25,39 @@ import ( "go.opentelemetry.io/otel/metric/instrument/syncint64" ) -func NewNoopMeterProvider() MeterProvider { +// NewNoopMeterProvider creates a MeterProvider that does not record any metrics. +func NewNoopMeterProvider() metric.MeterProvider { return noopMeterProvider{} } type noopMeterProvider struct{} -var _ MeterProvider = noopMeterProvider{} +var _ metric.MeterProvider = noopMeterProvider{} -func (noopMeterProvider) Meter(instrumentationName string, opts ...MeterOption) Meter { +func (noopMeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { + return noopMeter{} +} + +// NewNoopMeter creates a Meter that does not record any metrics. +func NewNoopMeter() metric.Meter { return noopMeter{} } type noopMeter struct{} -var _ Meter = noopMeter{} +var _ metric.Meter = noopMeter{} -func (noopMeter) AsyncInt64() asyncint64.Instruments { - return asyncint64.NewNoopInstruments() +func (noopMeter) AsyncInt64() asyncint64.InstrumentProvider { + return nonrecordingAsyncInt64Instrument{} } -func (noopMeter) AsyncFloat64() asyncfloat64.Instruments { - return asyncfloat64.NewNoopInstruments() +func (noopMeter) AsyncFloat64() asyncfloat64.InstrumentProvider { + return nonrecordingAsyncFloat64Instrument{} } -func (noopMeter) SyncInt64() syncint64.Instruments { - return syncint64.NewNoopInstruments() +func (noopMeter) SyncInt64() syncint64.InstrumentProvider { + return nonrecordingSyncInt64Instrument{} } -func (noopMeter) SyncFloat64() syncfloat64.Instruments { - return syncfloat64.NewNoopInstruments() +func (noopMeter) SyncFloat64() syncfloat64.InstrumentProvider { + return nonrecordingSyncFloat64Instrument{} } func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) error { return nil diff --git a/sdk/export/metric/go.mod b/sdk/export/metric/go.mod index 2b10328d6a7..415e71a87f9 100644 --- a/sdk/export/metric/go.mod +++ b/sdk/export/metric/go.mod @@ -17,8 +17,6 @@ replace go.opentelemetry.io/otel/example/opencensus => ../../../example/opencens 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 @@ -42,9 +40,8 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../metric replace go.opentelemetry.io/otel/trace => ../../../trace require ( - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk/metric v0.0.0-00010101000000-000000000000 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/sdk/metric v0.27.0 ) replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough diff --git a/sdk/go.mod b/sdk/go.mod index 548e5460134..bad9bd0c41c 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -8,8 +8,8 @@ require ( github.com/go-logr/logr v1.2.2 github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/trace v1.4.1 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 ) @@ -25,8 +25,6 @@ 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 diff --git a/sdk/internal/env/env.go b/sdk/internal/env/env.go index 397fd9593cc..df7a05626b3 100644 --- a/sdk/internal/env/env.go +++ b/sdk/internal/env/env.go @@ -40,6 +40,21 @@ const ( // Note: Must be less than or equal to EnvBatchSpanProcessorMaxQueueSize // i.e. 512 BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE" + + // SpanAttributesCountKey + // Maximum allowed span attribute count + // Default: 128 + SpanAttributesCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" + + // SpanEventCountKey + // Maximum allowed span event count + // Default: 128 + SpanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT" + + // SpanLinkCountKey + // Maximum allowed span link count + // Default: 128 + SpanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT" ) // IntEnvOr returns the int value of the environment variable with name key if diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index b206b7cbf83..fd5f49bd1ef 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -22,7 +22,9 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/metric/instrument" + "go.opentelemetry.io/otel/metric/instrument/syncfloat64" + "go.opentelemetry.io/otel/metric/instrument/syncint64" sdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/processor/processortest" @@ -44,7 +46,7 @@ func newFixture(b *testing.B) *benchFixture { } bf.accumulator = sdk.NewAccumulator(bf) - bf.meter = metric.WrapMeterImpl(bf.accumulator) + bf.meter = sdkapi.WrapMeterImpl(bf.accumulator) return bf } @@ -56,8 +58,33 @@ func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter { return f.meter } -func (f *benchFixture) meterMust() metric.MeterMust { - return metric.Must(f.meter) +func (f *benchFixture) iCounter(name string) syncint64.Counter { + ctr, err := f.meter.SyncInt64().Counter(name) + if err != nil { + f.B.Error(err) + } + return ctr +} +func (f *benchFixture) fCounter(name string) syncfloat64.Counter { + ctr, err := f.meter.SyncFloat64().Counter(name) + if err != nil { + f.B.Error(err) + } + return ctr +} +func (f *benchFixture) iHistogram(name string) syncint64.Histogram { + ctr, err := f.meter.SyncInt64().Histogram(name) + if err != nil { + f.B.Error(err) + } + return ctr +} +func (f *benchFixture) fHistogram(name string) syncfloat64.Histogram { + ctr, err := f.meter.SyncFloat64().Histogram(name) + if err != nil { + f.B.Error(err) + } + return ctr } func makeLabels(n int) []attribute.KeyValue { @@ -81,7 +108,7 @@ func benchmarkLabels(b *testing.B, n int) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(n) - cnt := fix.meterMust().NewInt64Counter("int64.sum") + cnt := fix.iCounter("int64.sum") b.ResetTimer() @@ -154,31 +181,33 @@ func BenchmarkIterator_16(b *testing.B) { // 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) +// TODO readd global - sdk := global.Meter("test") - global.SetMeterProvider(fix) +// 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) - labs := []attribute.KeyValue{attribute.String("A", "B")} - cnt := Must(sdk).NewInt64Counter("int64.sum") +// sdk := global.Meter("test") +// global.SetMeterProvider(fix) - b.ResetTimer() +// labs := []attribute.KeyValue{attribute.String("A", "B")} +// cnt := Must(sdk).NewInt64Counter("int64.sum") - for i := 0; i < b.N; i++ { - cnt.Add(ctx, 1, labs...) - } -} +// 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") + cnt := fix.iCounter("int64.sum") b.ResetTimer() @@ -191,7 +220,7 @@ func BenchmarkFloat64CounterAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - cnt := fix.meterMust().NewFloat64Counter("float64.sum") + cnt := fix.fCounter("float64.sum") b.ResetTimer() @@ -206,7 +235,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meterMust().NewInt64Histogram("int64.lastvalue") + mea := fix.iHistogram("int64.lastvalue") b.ResetTimer() @@ -219,7 +248,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meterMust().NewFloat64Histogram("float64.lastvalue") + mea := fix.fHistogram("float64.lastvalue") b.ResetTimer() @@ -234,7 +263,7 @@ func BenchmarkInt64HistogramAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meterMust().NewInt64Histogram("int64.histogram") + mea := fix.iHistogram("int64.histogram") b.ResetTimer() @@ -247,7 +276,7 @@ func BenchmarkFloat64HistogramAdd(b *testing.B) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(1) - mea := fix.meterMust().NewFloat64Histogram("float64.histogram") + mea := fix.fHistogram("float64.histogram") b.ResetTimer() @@ -264,12 +293,12 @@ func BenchmarkObserverRegistration(b *testing.B) { 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) + ctr, _ := fix.meter.AsyncInt64().Counter(names[i]) + _ = fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(context.Context) {}) } } @@ -277,11 +306,16 @@ 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) { + ctr, _ := fix.meter.AsyncInt64().Counter("test.lastvalue") + err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) { for i := 0; i < b.N; i++ { - result.Observe((int64)(i), labs...) + ctr.Observe(ctx, (int64)(i), labs...) } }) + if err != nil { + b.Errorf("could not register callback: %v", err) + b.FailNow() + } b.ResetTimer() @@ -292,11 +326,16 @@ 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) { + ctr, _ := fix.meter.AsyncFloat64().Counter("test.lastvalue") + err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) { for i := 0; i < b.N; i++ { - result.Observe((float64)(i), labs...) + ctr.Observe(ctx, (float64)(i), labs...) } }) + if err != nil { + b.Errorf("could not register callback: %v", err) + b.FailNow() + } b.ResetTimer() @@ -310,17 +349,18 @@ func benchmarkBatchRecord8Labels(b *testing.B, numInst int) { ctx := context.Background() fix := newFixture(b) labs := makeLabels(numLabels) - var meas []sdkapi.Measurement + var meas []syncint64.Counter for i := 0; i < numInst; i++ { - inst := fix.meterMust().NewInt64Counter(fmt.Sprintf("int64.%d.sum", i)) - meas = append(meas, inst.Measurement(1)) + meas = append(meas, fix.iCounter(fmt.Sprintf("int64.%d.sum", i))) } b.ResetTimer() for i := 0; i < b.N; i++ { - fix.accumulator.RecordBatch(ctx, labs, meas...) + for _, ctr := range meas { + ctr.Add(ctx, 1, labs...) + } } } @@ -346,7 +386,7 @@ func BenchmarkRepeatedDirectCalls(b *testing.B) { ctx := context.Background() fix := newFixture(b) - c := fix.meterMust().NewInt64Counter("int64.sum") + c := fix.iCounter("int64.sum") k := attribute.String("bench", "true") b.ResetTimer() diff --git a/sdk/metric/controller/basic/pull_test.go b/sdk/metric/controller/basic/pull_test.go index 2df72b967cc..2284e68a71d 100644 --- a/sdk/metric/controller/basic/pull_test.go +++ b/sdk/metric/controller/basic/pull_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest" diff --git a/sdk/metric/correct_test.go b/sdk/metric/correct_test.go index 57550c29ccb..1b24a209c76 100644 --- a/sdk/metric/correct_test.go +++ b/sdk/metric/correct_test.go @@ -26,6 +26,9 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/instrument" + "go.opentelemetry.io/otel/metric/instrument/asyncint64" + "go.opentelemetry.io/otel/metric/nonrecording" metricsdk "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/export" @@ -34,8 +37,6 @@ import ( "go.opentelemetry.io/otel/sdk/metric/sdkapi" ) -var Must = metric.Must - type handler struct { sync.Mutex err error @@ -88,7 +89,7 @@ func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector, accum := metricsdk.NewAccumulator( processor, ) - meter := metric.WrapMeterImpl(accum) + meter := sdkapi.WrapMeterImpl(accum) return meter, accum, testSelector, processor } @@ -96,7 +97,8 @@ func TestInputRangeCounter(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) - counter := Must(meter).NewInt64Counter("name.sum") + counter, err := meter.SyncInt64().Counter("name.sum") + require.NoError(t, err) counter.Add(ctx, -1) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) @@ -118,7 +120,8 @@ func TestInputRangeUpDownCounter(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) - counter := Must(meter).NewInt64UpDownCounter("name.sum") + counter, err := meter.SyncInt64().UpDownCounter("name.sum") + require.NoError(t, err) counter.Add(ctx, -1) counter.Add(ctx, -1) @@ -137,7 +140,8 @@ func TestInputRangeHistogram(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) - histogram := Must(meter).NewFloat64Histogram("name.histogram") + histogram, err := meter.SyncFloat64().Histogram("name.histogram") + require.NoError(t, err) histogram.Record(ctx, math.NaN()) require.Equal(t, aggregation.ErrNaNInput, testHandler.Flush()) @@ -162,7 +166,8 @@ func TestDisabledInstrument(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) - histogram := Must(meter).NewFloat64Histogram("name.disabled") + histogram, err := meter.SyncFloat64().Histogram("name.disabled") + require.NoError(t, err) histogram.Record(ctx, -1) checkpointed := sdk.Collect(ctx) @@ -175,7 +180,8 @@ func TestRecordNaN(t *testing.T) { ctx := context.Background() meter, _, _, _ := newSDK(t) - c := Must(meter).NewFloat64Counter("name.sum") + c, err := meter.SyncFloat64().Counter("name.sum") + require.NoError(t, err) require.Nil(t, testHandler.Flush()) c.Add(ctx, math.NaN()) @@ -186,7 +192,8 @@ func TestSDKLabelsDeduplication(t *testing.T) { ctx := context.Background() meter, sdk, _, processor := newSDK(t) - counter := Must(meter).NewInt64Counter("name.sum") + counter, err := meter.SyncInt64().Counter("name.sum") + require.NoError(t, err) const ( maxKeys = 21 @@ -277,48 +284,86 @@ func TestObserverCollection(t *testing.T) { 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")) + gaugeF, err := meter.AsyncFloat64().Gauge("float.gauge.lastvalue") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + gaugeF, + }, func(ctx context.Context) { + gaugeF.Observe(ctx, 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")) + gaugeF.Observe(ctx, float64(-mult), attribute.String("A", "B")) + gaugeF.Observe(ctx, 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)) + require.NoError(t, err) + + gaugeI, err := meter.AsyncInt64().Gauge("int.gauge.lastvalue") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + gaugeI, + }, func(ctx context.Context) { + gaugeI.Observe(ctx, int64(-mult), attribute.String("A", "B")) + gaugeI.Observe(ctx, int64(mult)) // last value wins - result.Observe(int64(mult), attribute.String("A", "B")) - result.Observe(int64(mult)) + gaugeI.Observe(ctx, int64(mult), attribute.String("A", "B")) + gaugeI.Observe(ctx, 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")) + require.NoError(t, err) + + counterF, err := meter.AsyncFloat64().Counter("float.counterobserver.sum") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + counterF, + }, func(ctx context.Context) { + counterF.Observe(ctx, float64(mult), attribute.String("A", "B")) + counterF.Observe(ctx, float64(2*mult), attribute.String("A", "B")) + counterF.Observe(ctx, 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)) + require.NoError(t, err) + + counterI, err := meter.AsyncInt64().Counter("int.counterobserver.sum") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + counterI, + }, func(ctx context.Context) { + counterI.Observe(ctx, int64(2*mult), attribute.String("A", "B")) + counterI.Observe(ctx, int64(mult)) // last value wins - result.Observe(int64(mult), attribute.String("A", "B")) - result.Observe(int64(mult)) + counterI.Observe(ctx, int64(mult), attribute.String("A", "B")) + counterI.Observe(ctx, 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")) + require.NoError(t, err) + + updowncounterF, err := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + updowncounterF, + }, func(ctx context.Context) { + updowncounterF.Observe(ctx, float64(mult), attribute.String("A", "B")) + updowncounterF.Observe(ctx, float64(-2*mult), attribute.String("A", "B")) + updowncounterF.Observe(ctx, 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)) + require.NoError(t, err) + + updowncounterI, err := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + updowncounterI, + }, func(ctx context.Context) { + updowncounterI.Observe(ctx, int64(2*mult), attribute.String("A", "B")) + updowncounterI.Observe(ctx, int64(mult)) // last value wins - result.Observe(int64(mult), attribute.String("A", "B")) - result.Observe(int64(-mult)) + updowncounterI.Observe(ctx, int64(mult), attribute.String("A", "B")) + updowncounterI.Observe(ctx, int64(-mult)) }) + require.NoError(t, err) - _ = Must(meter).NewInt64GaugeObserver("empty.gauge.sum", func(_ context.Context, result metric.Int64ObserverResult) { + unused, err := meter.AsyncInt64().Gauge("empty.gauge.sum") + require.NoError(t, err) + err = meter.RegisterCallback([]instrument.Asynchronous{ + unused, + }, func(ctx context.Context) { }) + require.NoError(t, err) for mult = 0; mult < 3; mult++ { processor.Reset() @@ -333,15 +378,15 @@ func TestObserverCollection(t *testing.T) { "int.gauge.lastvalue//": mult, "int.gauge.lastvalue/A=B/": mult, - "float.counterobserver.sum/A=B/": 2 * mult, + "float.counterobserver.sum/A=B/": 3 * mult, "float.counterobserver.sum/C=D/": mult, - "int.counterobserver.sum//": mult, - "int.counterobserver.sum/A=B/": mult, + "int.counterobserver.sum//": 2 * mult, + "int.counterobserver.sum/A=B/": 3 * mult, - "float.updowncounterobserver.sum/A=B/": -2 * mult, + "float.updowncounterobserver.sum/A=B/": -mult, "float.updowncounterobserver.sum/C=D/": mult, - "int.updowncounterobserver.sum//": -mult, - "int.updowncounterobserver.sum/A=B/": mult, + "int.updowncounterobserver.sum//": 0, + "int.updowncounterobserver.sum/A=B/": 3 * mult, }, processor.Values()) } } @@ -351,18 +396,26 @@ func TestCounterObserverInputRange(t *testing.T) { 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")) + counterF, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum") + err := meter.RegisterCallback([]instrument.Asynchronous{ + counterF, + }, func(ctx context.Context) { + counterF.Observe(ctx, -2, attribute.String("A", "B")) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) - result.Observe(-1, attribute.String("C", "D")) + counterF.Observe(ctx, -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.NoError(t, err) + counterI, _ := meter.AsyncInt64().Counter("int.counterobserver.sum") + err = meter.RegisterCallback([]instrument.Asynchronous{ + counterI, + }, func(ctx context.Context) { + counterI.Observe(ctx, -1, attribute.String("A", "B")) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) - result.Observe(-1) + counterI.Observe(ctx, -1) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) }) + require.NoError(t, err) collected := sdk.Collect(ctx) @@ -377,51 +430,43 @@ 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") + floatGaugeObs, _ := meter.AsyncFloat64().Gauge("float.gauge.lastvalue") + intGaugeObs, _ := meter.AsyncInt64().Gauge("int.gauge.lastvalue") + floatCounterObs, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum") + intCounterObs, _ := meter.AsyncInt64().Counter("int.counterobserver.sum") + floatUpDownCounterObs, _ := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum") + intUpDownCounterObs, _ := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum") + + err := meter.RegisterCallback([]instrument.Asynchronous{ + floatGaugeObs, + intGaugeObs, + floatCounterObs, + intCounterObs, + floatUpDownCounterObs, + intUpDownCounterObs, + }, func(ctx context.Context) { + ab := attribute.String("A", "B") + floatGaugeObs.Observe(ctx, 1, ab) + floatGaugeObs.Observe(ctx, -1, ab) + intGaugeObs.Observe(ctx, -1, ab) + intGaugeObs.Observe(ctx, 1, ab) + floatCounterObs.Observe(ctx, 1000, ab) + intCounterObs.Observe(ctx, 100, ab) + floatUpDownCounterObs.Observe(ctx, -1000, ab) + intUpDownCounterObs.Observe(ctx, -100, ab) + + cd := attribute.String("C", "D") + floatGaugeObs.Observe(ctx, -1, cd) + floatCounterObs.Observe(ctx, -1, cd) + floatUpDownCounterObs.Observe(ctx, -1, cd) + + intGaugeObs.Observe(ctx, 1) + intGaugeObs.Observe(ctx, 1) + intCounterObs.Observe(ctx, 10) + floatCounterObs.Observe(ctx, 1.1) + intUpDownCounterObs.Observe(ctx, 10) + }) + require.NoError(t, err) collected := sdk.Collect(ctx) @@ -445,37 +490,6 @@ func TestObserverBatch(t *testing.T) { }, 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.histogram") - histogram2 := Must(meter).NewFloat64Histogram("float64.histogram") - - 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.histogram/A=B,C=D/": 3, - "float64.histogram/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. @@ -483,7 +497,9 @@ func TestRecordPersistence(t *testing.T) { ctx := context.Background() meter, sdk, selector, _ := newSDK(t) - c := Must(meter).NewFloat64Counter("name.sum") + c, err := meter.SyncFloat64().Counter("name.sum") + require.NoError(t, err) + uk := attribute.String("bound", "false") for i := 0; i < 100; i++ { @@ -497,51 +513,58 @@ func TestRecordPersistence(t *testing.T) { func TestIncorrectInstruments(t *testing.T) { // The Batch observe/record APIs are susceptible to // uninitialized instruments. - var counter metric.Int64Counter - var observer metric.Int64GaugeObserver + var observer asyncint64.Gauge 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)) + err := meter.RegisterCallback([]instrument.Asynchronous{ + observer, + }, func(ctx context.Context) { + observer.Observe(ctx, 1) }) + require.ErrorIs(t, err, metricsdk.ErrBadInstrument) collected := sdk.Collect(ctx) - require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush()) + err = testHandler.Flush() + require.NoError(t, err) 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)) - }) + noopMeter := nonrecording.NewNoopMeter() + observer, _ = noopMeter.AsyncInt64().Gauge("observer") + + err = meter.RegisterCallback( + []instrument.Asynchronous{observer}, + func(ctx context.Context) { + observer.Observe(ctx, 1) + }, + ) + require.ErrorIs(t, err, metricsdk.ErrBadInstrument) collected = sdk.Collect(ctx) require.Equal(t, 0, collected) require.EqualValues(t, map[string]float64{}, processor.Values()) - require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush()) + + err = testHandler.Flush() + require.NoError(t, err) } 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) - }, - ) + counter, _ := meter.SyncFloat64().Counter("counter.sum") + gauge, _ := meter.AsyncInt64().Gauge("observer.lastvalue") + + err := meter.RegisterCallback([]instrument.Asynchronous{ + gauge, + }, func(ctx context.Context) { + gauge.Observe(ctx, 10) + counter.Add(ctx, 100) + }) + require.NoError(t, err) sdk.Collect(ctx) diff --git a/sdk/metric/go.mod b/sdk/metric/go.mod index 1424e5674cf..33fda254764 100644 --- a/sdk/metric/go.mod +++ b/sdk/metric/go.mod @@ -16,8 +16,6 @@ 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 @@ -43,10 +41,9 @@ replace go.opentelemetry.io/otel/trace => ../../trace require ( github.com/benbjohnson/clock v1.3.0 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 - go.opentelemetry.io/otel/internal/metric v0.26.0 - go.opentelemetry.io/otel/metric v0.26.0 - go.opentelemetry.io/otel/sdk v1.3.0 + go.opentelemetry.io/otel v1.4.1 + go.opentelemetry.io/otel/metric v0.27.0 + go.opentelemetry.io/otel/sdk v1.4.1 ) replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough @@ -57,8 +54,6 @@ replace go.opentelemetry.io/otel/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 diff --git a/sdk/metric/sdk.go b/sdk/metric/sdk.go index ee127e6fa19..db3ad330213 100644 --- a/sdk/metric/sdk.go +++ b/sdk/metric/sdk.go @@ -98,7 +98,7 @@ type ( // supports checking for no updates during a round. collectedCount int64 - // storage is the stored label set for this record, + // labels is the stored label set for this record, // except in cases where a label set is shared due to // batch recording. labels attribute.Set @@ -183,7 +183,7 @@ func (b *baseInstrument) acquireHandle(kvs []attribute.KeyValue) *record { // an interface here. if actual, loaded := b.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 allocationb. + // 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 @@ -208,6 +208,8 @@ func (b *baseInstrument) acquireHandle(kvs []attribute.KeyValue) *record { } } +// RecordOne captures a single synchronous metric event. +// // 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) @@ -215,7 +217,9 @@ func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs [ h.captureOne(ctx, num) } -// @@@ +// ObserveOne captures a single asynchronous metric event. + +// The order of the input array `kvs` may be sorted after the function is called. func (a *asyncInstrument) ObserveOne(ctx context.Context, num number.Number, attrs []attribute.KeyValue) { h := a.acquireHandle(attrs) defer h.unbind() diff --git a/sdk/metric/sdkapi/wrap.go b/sdk/metric/sdkapi/wrap.go index ec05afc207e..9a4d5b0de14 100644 --- a/sdk/metric/sdkapi/wrap.go +++ b/sdk/metric/sdkapi/wrap.go @@ -54,19 +54,19 @@ func UnwrapMeterImpl(m metric.Meter) MeterImpl { return mm.MeterImpl } -func (m meter) AsyncFloat64() asyncfloat64.Instruments { +func (m meter) AsyncFloat64() asyncfloat64.InstrumentProvider { return afMeter{m} } -func (m meter) AsyncInt64() asyncint64.Instruments { +func (m meter) AsyncInt64() asyncint64.InstrumentProvider { return aiMeter{m} } -func (m meter) SyncFloat64() syncfloat64.Instruments { +func (m meter) SyncFloat64() syncfloat64.InstrumentProvider { return sfMeter{m} } -func (m meter) SyncInt64() syncint64.Instruments { +func (m meter) SyncInt64() syncint64.InstrumentProvider { return siMeter{m} } diff --git a/sdk/metric/stress_test.go b/sdk/metric/stress_test.go deleted file mode 100644 index 88f1245ec18..00000000000 --- a/sdk/metric/stress_test.go +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// 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/sdk/metric/export" - "go.opentelemetry.io/otel/sdk/metric/export/aggregation" - "go.opentelemetry.io/otel/sdk/metric/number" - "go.opentelemetry.io/otel/sdk/metric/processor/processortest" - "go.opentelemetry.io/otel/sdk/metric/sdkapi" -) - -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()) -} diff --git a/sdk/resource/process.go b/sdk/resource/process.go index 80d5e699323..c1e5ae90a5b 100644 --- a/sdk/resource/process.go +++ b/sdk/resource/process.go @@ -39,7 +39,12 @@ var ( defaultExecutablePathProvider executablePathProvider = os.Executable defaultCommandArgsProvider commandArgsProvider = func() []string { return os.Args } defaultOwnerProvider ownerProvider = user.Current - defaultRuntimeNameProvider runtimeNameProvider = func() string { return runtime.Compiler } + defaultRuntimeNameProvider runtimeNameProvider = func() string { + if runtime.Compiler == "gc" { + return "go" + } + return runtime.Compiler + } defaultRuntimeVersionProvider runtimeVersionProvider = runtime.Version defaultRuntimeOSProvider runtimeOSProvider = func() string { return runtime.GOOS } defaultRuntimeArchProvider runtimeArchProvider = func() string { return runtime.GOARCH } diff --git a/sdk/resource/process_test.go b/sdk/resource/process_test.go index d60c48411c4..0f9c628cc8d 100644 --- a/sdk/resource/process_test.go +++ b/sdk/resource/process_test.go @@ -118,7 +118,11 @@ func TestCommandArgs(t *testing.T) { } func TestRuntimeName(t *testing.T) { - require.EqualValues(t, runtime.Compiler, resource.RuntimeName()) + if runtime.Compiler == "gc" { + require.EqualValues(t, "go", resource.RuntimeName()) + } else { + require.EqualValues(t, runtime.Compiler, resource.RuntimeName()) + } } func TestRuntimeOS(t *testing.T) { diff --git a/sdk/trace/attributesmap.go b/sdk/trace/attributesmap.go deleted file mode 100644 index b891c8178b7..00000000000 --- a/sdk/trace/attributesmap.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package 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) - } -} diff --git a/sdk/trace/attributesmap_test.go b/sdk/trace/attributesmap_test.go deleted file mode 100644 index 90a201e2195..00000000000 --- a/sdk/trace/attributesmap_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package 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() - } -} diff --git a/sdk/trace/batch_span_processor.go b/sdk/trace/batch_span_processor.go index 6a7b9dc31bd..56847d9ccba 100644 --- a/sdk/trace/batch_span_processor.go +++ b/sdk/trace/batch_span_processor.go @@ -250,7 +250,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { } if l := len(bsp.batch); l > 0 { - global.Debug("exporting spans", "count", len(bsp.batch), "dropped", bsp.dropped) + global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped)) err := bsp.e.ExportSpans(ctx, bsp.batch) // A new batch is always created after exporting, even if the batch failed to be exported. diff --git a/sdk/trace/benchmark_test.go b/sdk/trace/benchmark_test.go index 335deaf4fc3..81a06e2f8f9 100644 --- a/sdk/trace/benchmark_test.go +++ b/sdk/trace/benchmark_test.go @@ -16,6 +16,7 @@ package trace_test import ( "context" + "fmt" "testing" "time" @@ -24,6 +25,28 @@ import ( "go.opentelemetry.io/otel/trace" ) +func BenchmarkSpanSetAttributesOverCapacity(b *testing.B) { + tp := sdktrace.NewTracerProvider( + sdktrace.WithSpanLimits(sdktrace.SpanLimits{AttributeCountLimit: 1}), + ) + tracer := tp.Tracer("BenchmarkSpanSetAttributesOverCapacity") + ctx := context.Background() + attrs := make([]attribute.KeyValue, 128) + for i := range attrs { + key := fmt.Sprintf("key-%d", i) + attrs[i] = attribute.Bool(key, true) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, span := tracer.Start(ctx, "/foo") + span.SetAttributes(attrs...) + span.End() + } +} + func BenchmarkStartEndSpan(b *testing.B) { traceBenchmark(b, "Benchmark StartEndSpan", func(b *testing.B, t trace.Tracer) { ctx := context.Background() diff --git a/sdk/trace/config.go b/sdk/trace/config.go index 61a30439251..efea7b302f9 100644 --- a/sdk/trace/config.go +++ b/sdk/trace/config.go @@ -13,6 +13,7 @@ // limitations under the License. package trace // import "go.opentelemetry.io/otel/sdk/trace" +import "go.opentelemetry.io/otel/sdk/internal/env" // SpanLimits represents the limits of a span. type SpanLimits struct { @@ -50,14 +51,29 @@ func (sl *SpanLimits) ensureDefault() { } } +func (sl *SpanLimits) parsePotentialEnvConfigs() { + sl.AttributeCountLimit = env.IntEnvOr(env.SpanAttributesCountKey, sl.AttributeCountLimit) + sl.LinkCountLimit = env.IntEnvOr(env.SpanLinkCountKey, sl.LinkCountLimit) + sl.EventCountLimit = env.IntEnvOr(env.SpanEventCountKey, sl.EventCountLimit) +} + const ( // DefaultAttributeCountLimit is the default maximum allowed span attribute count. + // If not specified via WithSpanLimits, will try to retrieve the value from + // environment variable `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT`. + // If Invalid value (negative or zero) is provided, the default value 128 will be used. DefaultAttributeCountLimit = 128 // DefaultEventCountLimit is the default maximum allowed span event count. + // If not specified via WithSpanLimits, will try to retrieve the value from + // environment variable `OTEL_SPAN_EVENT_COUNT_LIMIT`. + // If Invalid value (negative or zero) is provided, the default value 128 will be used. DefaultEventCountLimit = 128 // DefaultLinkCountLimit is the default maximum allowed span link count. + // If the value is not specified via WithSpanLimits, will try to retrieve the value from + // environment variable `OTEL_SPAN_LINK_COUNT_LIMIT`. + // If Invalid value (negative or zero) is provided, the default value 128 will be used. DefaultLinkCountLimit = 128 // DefaultAttributePerEventCountLimit is the default maximum allowed attribute per span event count. diff --git a/sdk/trace/provider.go b/sdk/trace/provider.go index c6b311f9cdc..2de79f03397 100644 --- a/sdk/trace/provider.go +++ b/sdk/trace/provider.go @@ -98,6 +98,7 @@ var _ trace.TracerProvider = &TracerProvider{} func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { o := tracerProviderConfig{} + o.spanLimits.parsePotentialEnvConfigs() for _, opt := range opts { o = opt.apply(o) } diff --git a/sdk/trace/provider_test.go b/sdk/trace/provider_test.go index e2fce31d7f7..0633889bdc2 100644 --- a/sdk/trace/provider_test.go +++ b/sdk/trace/provider_test.go @@ -17,8 +17,14 @@ package trace import ( "context" "errors" + "os" "testing" + "github.com/stretchr/testify/require" + + ottest "go.opentelemetry.io/otel/internal/internaltest" + "go.opentelemetry.io/otel/sdk/internal/env" + "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/trace" @@ -94,3 +100,58 @@ func TestSchemaURL(t *testing.T) { tracerStruct := tracerIface.(*tracer) assert.EqualValues(t, schemaURL, tracerStruct.instrumentationLibrary.SchemaURL) } + +func TestNewTraceProviderWithoutSpanLimitConfiguration(t *testing.T) { + envStore := ottest.NewEnvStore() + defer func() { + require.NoError(t, envStore.Restore()) + }() + envStore.Record(env.SpanAttributesCountKey) + envStore.Record(env.SpanEventCountKey) + envStore.Record(env.SpanLinkCountKey) + require.NoError(t, os.Setenv(env.SpanEventCountKey, "111")) + require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "222")) + require.NoError(t, os.Setenv(env.SpanLinkCountKey, "333")) + tp := NewTracerProvider() + assert.Equal(t, 111, tp.spanLimits.EventCountLimit) + assert.Equal(t, 222, tp.spanLimits.AttributeCountLimit) + assert.Equal(t, 333, tp.spanLimits.LinkCountLimit) +} + +func TestNewTraceProviderWithSpanLimitConfigurationFromOptsAndEnvironmentVariable(t *testing.T) { + envStore := ottest.NewEnvStore() + defer func() { + require.NoError(t, envStore.Restore()) + }() + envStore.Record(env.SpanAttributesCountKey) + envStore.Record(env.SpanEventCountKey) + envStore.Record(env.SpanLinkCountKey) + require.NoError(t, os.Setenv(env.SpanEventCountKey, "111")) + require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "222")) + require.NoError(t, os.Setenv(env.SpanLinkCountKey, "333")) + tp := NewTracerProvider(WithSpanLimits(SpanLimits{ + EventCountLimit: 1, + AttributeCountLimit: 2, + LinkCountLimit: 3, + })) + assert.Equal(t, 1, tp.spanLimits.EventCountLimit) + assert.Equal(t, 2, tp.spanLimits.AttributeCountLimit) + assert.Equal(t, 3, tp.spanLimits.LinkCountLimit) +} + +func TestNewTraceProviderWithInvalidSpanLimitConfigurationFromEnvironmentVariable(t *testing.T) { + envStore := ottest.NewEnvStore() + defer func() { + require.NoError(t, envStore.Restore()) + }() + envStore.Record(env.SpanAttributesCountKey) + envStore.Record(env.SpanEventCountKey) + envStore.Record(env.SpanLinkCountKey) + require.NoError(t, os.Setenv(env.SpanEventCountKey, "-111")) + require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "-222")) + require.NoError(t, os.Setenv(env.SpanLinkCountKey, "-333")) + tp := NewTracerProvider() + assert.Equal(t, 128, tp.spanLimits.EventCountLimit) + assert.Equal(t, 128, tp.spanLimits.AttributeCountLimit) + assert.Equal(t, 128, tp.spanLimits.LinkCountLimit) +} diff --git a/sdk/trace/simple_span_processor.go b/sdk/trace/simple_span_processor.go index d31d3c1caff..e8530a95932 100644 --- a/sdk/trace/simple_span_processor.go +++ b/sdk/trace/simple_span_processor.go @@ -116,7 +116,7 @@ func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { return nil } -// MarshalLog is the marshaling function used by the logging system to represent this exporter. +// MarshalLog is the marshaling function used by the logging system to represent this Span Processor. func (ssp *simpleSpanProcessor) MarshalLog() interface{} { return struct { Type string diff --git a/sdk/trace/span.go b/sdk/trace/span.go index bf0c41c1112..779cde691dd 100644 --- a/sdk/trace/span.go +++ b/sdk/trace/span.go @@ -54,6 +54,7 @@ type ReadOnlySpan interface { // the span has not ended. EndTime() time.Time // Attributes returns the defining attributes of the span. + // The order of the returned attributes is not guaranteed to be stable across invocations. Attributes() []attribute.KeyValue // Links returns all the links the span has to other spans. Links() []Link @@ -129,9 +130,14 @@ type recordingSpan struct { // 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 + // attributes is a collection of user provided key/values. The collection + // is constrained by a configurable maximum held by the parent + // TracerProvider. When additional attributes are added after this maximum + // is reached these attributes the user is attempting to add are dropped. + // This dropped number of attributes is tracked and reported in the + // ReadOnlySpan exported when the span ends. + attributes []attribute.KeyValue + droppedAttributes int // events are stored in FIFO queue capped by configured limit. events evictedQueue @@ -194,11 +200,80 @@ func (s *recordingSpan) SetStatus(code codes.Code, description string) { // will be overwritten with the value contained in attributes. // // If this span is not being recorded than this method does nothing. +// +// If adding attributes to the span would exceed the maximum amount of +// attributes the span is configured to have, the last added attributes will +// be dropped. func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { if !s.IsRecording() { return } - s.copyToCappedAttributes(attributes...) + + s.mu.Lock() + defer s.mu.Unlock() + + // If adding these attributes could exceed the capacity of s perform a + // de-duplication and truncation while adding to avoid over allocation. + if len(s.attributes)+len(attributes) > s.tracer.provider.spanLimits.AttributeCountLimit { + s.addOverCapAttrs(attributes) + return + } + + // Otherwise, add without deduplication. When attributes are read they + // will be deduplicated, optimizing the operation. + for _, a := range attributes { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + s.attributes = append(s.attributes, a) + } +} + +// addOverCapAttrs adds the attributes attrs to the span s while +// de-duplicating the attributes of s and attrs and dropping attributes that +// exceed the capacity of s. +// +// This method assumes s.mu.Lock is held by the caller. +// +// This method should only be called when there is a possibility that adding +// attrs to s will exceed the capacity of s. Otherwise, attrs should be added +// to s without checking for duplicates and all retrieval methods of the +// attributes for s will de-duplicate as needed. +func (s *recordingSpan) addOverCapAttrs(attrs []attribute.KeyValue) { + // In order to not allocate more capacity to s.attributes than needed, + // prune and truncate this addition of attributes while adding. + + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) + + // Now that s.attributes is deduplicated, adding unique attributes up to + // the capacity of s will not over allocate s.attributes. + for _, a := range attrs { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + + if idx, ok := exists[a.Key]; ok { + // Perform all updates before dropping, even when at capacity. + s.attributes[idx] = a + continue + } + + if len(s.attributes) >= s.tracer.provider.spanLimits.AttributeCountLimit { + // Do not just drop all of the remaining attributes, make sure + // updates are checked and performed. + s.droppedAttributes++ + } else { + s.attributes = append(s.attributes, a) + exists[a.Key] = len(s.attributes) - 1 + } + } } // End ends the span. This method does nothing if the span is already ended or @@ -388,13 +463,45 @@ func (s *recordingSpan) EndTime() time.Time { } // Attributes returns the attributes of this span. +// +// The order of the returned attributes is not guaranteed to be stable. func (s *recordingSpan) Attributes() []attribute.KeyValue { s.mu.Lock() defer s.mu.Unlock() - if s.attributes.evictList.Len() == 0 { - return []attribute.KeyValue{} + s.dedupeAttrs() + return s.attributes +} + +// dedupeAttrs deduplicates the attributes of s to fit capacity. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrs() { + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) +} + +// dedupeAttrsFromRecord deduplicates the attributes of s to fit capacity +// using record as the record of unique attribute keys to their index. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrsFromRecord(record *map[attribute.Key]int) { + // Use the fact that slices share the same backing array. + unique := s.attributes[:0] + for _, a := range s.attributes { + if idx, ok := (*record)[a.Key]; ok { + unique[idx] = a + } else { + unique = append(unique, a) + (*record)[a.Key] = len(unique) - 1 + } } - return s.attributes.toKeyValue() + // s.attributes have element types of attribute.KeyValue. These types are + // not pointers and they themselves do not contain pointer fields, + // therefore the duplicate values do not need to be zeroed for them to be + // garbage collected. + s.attributes = unique } // Links returns the links of this span. @@ -463,7 +570,7 @@ func (s *recordingSpan) addLink(link trace.Link) { func (s *recordingSpan) DroppedAttributes() int { s.mu.Lock() defer s.mu.Unlock() - return s.attributes.droppedCount + return s.droppedAttributes } // DroppedLinks returns the number of links dropped by the span due to limits @@ -513,10 +620,11 @@ func (s *recordingSpan) snapshot() ReadOnlySpan { 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.attributes) > 0 { + s.dedupeAttrs() + sd.attributes = s.attributes } + sd.droppedAttributeCount = s.droppedAttributes if len(s.events.queue) > 0 { sd.events = s.interfaceArrayToEventArray() sd.droppedEventCount = s.events.droppedCount @@ -544,18 +652,6 @@ func (s *recordingSpan) interfaceArrayToEventArray() []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 diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go index be27ed62041..5ba0015584a 100644 --- a/sdk/trace/trace_test.go +++ b/sdk/trace/trace_test.go @@ -419,34 +419,6 @@ func TestSetSpanAttributesOnStart(t *testing.T) { } } -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() @@ -469,72 +441,193 @@ func TestSamplerAttributesLocalChildSpan(t *testing.T) { 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), +func TestSpanSetAttributes(t *testing.T) { + attrs := [...]attribute.KeyValue{ + attribute.String("key1", "value1"), 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) + attribute.String("key3", "value3"), + attribute.String("key4", "value4"), + attribute.String("key1", "value5"), + attribute.String("key2", "value6"), + attribute.String("key3", "value7"), } + invalid := attribute.KeyValue{} - 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), + tests := []struct { + name string + input [][]attribute.KeyValue + wantAttrs []attribute.KeyValue + wantDropped int + }{ + { + name: "array", + input: [][]attribute.KeyValue{attrs[:3]}, + wantAttrs: attrs[:3], + }, + { + name: "single_value:array", + input: [][]attribute.KeyValue{attrs[:1], attrs[1:3]}, + wantAttrs: attrs[:3], + }, + { + name: "array:single_value", + input: [][]attribute.KeyValue{attrs[:2], attrs[2:3]}, + wantAttrs: attrs[:3], + }, + { + name: "single_values", + input: [][]attribute.KeyValue{attrs[:1], attrs[1:2], attrs[2:3]}, + wantAttrs: attrs[:3], }, - 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())) + // The tracing specification states: + // + // For each unique attribute key, addition of which would result in + // exceeding the limit, SDK MUST discard that key/value pair + // + // Therefore, adding attributes after the capacity is reached should + // result in those attributes being dropped. - span := startSpan(tp, "SpanToSetInvalidKeyOrValue") - span.SetAttributes( - attribute.Bool("", true), - attribute.Bool("key1", false), - ) - got, err := endSpan(te, span) - if err != nil { - t.Fatal(err) - } + { + name: "drop_last_added", + input: [][]attribute.KeyValue{attrs[:3], attrs[3:4], attrs[3:4]}, + wantAttrs: attrs[:3], + wantDropped: 2, + }, - want := &snapshot{ - spanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: tid, - TraceFlags: 0x1, - }), - parent: sc.WithRemote(true), - name: "span0", - attributes: []attribute.KeyValue{ - attribute.Bool("key1", false), + // The tracing specification states: + // + // Setting an attribute with the same key as an existing attribute + // SHOULD overwrite the existing attribute's value. + // + // Therefore, attributes are updated regardless of capacity state. + + { + name: "single_value_update", + input: [][]attribute.KeyValue{attrs[:1], attrs[:3]}, + wantAttrs: attrs[:3], + }, + { + name: "all_update", + input: [][]attribute.KeyValue{attrs[:3], attrs[4:7]}, + wantAttrs: attrs[4:7], + }, + { + name: "all_update/multi", + input: [][]attribute.KeyValue{attrs[:3], attrs[4:7], attrs[:3]}, + wantAttrs: attrs[:3], + }, + { + name: "deduplicate/under_capacity", + input: [][]attribute.KeyValue{attrs[:1], attrs[:1], attrs[:1]}, + wantAttrs: attrs[:1], + }, + { + name: "deduplicate/over_capacity", + input: [][]attribute.KeyValue{attrs[:1], attrs[:1], attrs[:1], attrs[:3]}, + wantAttrs: attrs[:3], + }, + { + name: "deduplicate/added", + input: [][]attribute.KeyValue{ + attrs[:2], + {attrs[2], attrs[2], attrs[2]}, + }, + wantAttrs: attrs[:3], + }, + { + name: "deduplicate/added_at_cappacity", + input: [][]attribute.KeyValue{ + attrs[:3], + {attrs[2], attrs[2], attrs[2]}, + }, + wantAttrs: attrs[:3], + }, + { + name: "invalid", + input: [][]attribute.KeyValue{ + {invalid}, + }, + wantDropped: 1, + }, + { + name: "invalid_with_valid", + input: [][]attribute.KeyValue{ + {invalid, attrs[0]}, + }, + wantAttrs: attrs[:1], + wantDropped: 1, + }, + { + name: "invalid_over_capacity", + input: [][]attribute.KeyValue{ + {invalid, invalid, invalid, invalid, attrs[0]}, + }, + wantAttrs: attrs[:1], + wantDropped: 4, + }, + { + name: "valid:invalid/under_capacity", + input: [][]attribute.KeyValue{ + attrs[:1], + {invalid}, + }, + wantAttrs: attrs[:1], + wantDropped: 1, + }, + { + name: "valid:invalid/over_capacity", + input: [][]attribute.KeyValue{ + attrs[:1], + {invalid, invalid, invalid, invalid}, + }, + wantAttrs: attrs[:1], + wantDropped: 4, + }, + { + name: "valid_at_capacity:invalid", + input: [][]attribute.KeyValue{ + attrs[:3], + {invalid, invalid, invalid, invalid}, + }, + wantAttrs: attrs[:3], + wantDropped: 4, }, - spanKind: trace.SpanKindInternal, - droppedAttributeCount: 0, - instrumentationLibrary: instrumentation.Library{Name: "SpanToSetInvalidKeyOrValue"}, } - if diff := cmpDiff(got, want); diff != "" { - t.Errorf("SetSpanAttributesWithInvalidKey: -got +want %s", diff) + + const ( + capacity = 3 + instName = "TestSpanAttributeCapacity" + spanName = "test span" + ) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + te := NewTestExporter() + tp := NewTracerProvider( + WithSyncer(te), + WithSpanLimits(SpanLimits{AttributeCountLimit: capacity}), + ) + _, span := tp.Tracer(instName).Start(context.Background(), spanName) + for _, a := range test.input { + span.SetAttributes(a...) + } + span.End() + + require.Implements(t, (*ReadOnlySpan)(nil), span) + roSpan := span.(ReadOnlySpan) + + // Ensure the span itself is valid. + assert.ElementsMatch(t, test.wantAttrs, roSpan.Attributes(), "exected attributes") + assert.Equal(t, test.wantDropped, roSpan.DroppedAttributes(), "dropped attributes") + + snap, ok := te.GetSpan(spanName) + require.Truef(t, ok, "span %s not exported", spanName) + + // Ensure the exported span snapshot is valid. + assert.ElementsMatch(t, test.wantAttrs, snap.Attributes(), "exected attributes") + assert.Equal(t, test.wantDropped, snap.DroppedAttributes(), "dropped attributes") + }) } } @@ -1852,3 +1945,11 @@ func TestWithIDGenerator(t *testing.T) { require.NoError(t, err) } } + +func TestEmptyRecordingSpanAttributes(t *testing.T) { + assert.Nil(t, (&recordingSpan{}).Attributes()) +} + +func TestEmptyRecordingSpanDroppedAttributes(t *testing.T) { + assert.Equal(t, 0, (&recordingSpan{}).DroppedAttributes()) +} diff --git a/sdk/trace/tracer.go b/sdk/trace/tracer.go index b63b4196516..5b8ab43be3d 100644 --- a/sdk/trace/tracer.go +++ b/sdk/trace/tracer.go @@ -122,12 +122,19 @@ func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr Sa } s := &recordingSpan{ + // Do not pre-allocate the attributes slice here! Doing so will + // allocate memory that is likely never going to be used, or if used, + // will be over-sized. The default Go compiler has been tested to + // dynamically allocate needed space very well. Benchmarking has shown + // it to be more performant than what we can predetermine here, + // especially for the common use case of few to no added + // attributes. + 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, diff --git a/trace/go.mod b/trace/go.mod index 1b19ac02308..e7a32932464 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -16,8 +16,6 @@ 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 @@ -43,7 +41,7 @@ replace go.opentelemetry.io/otel/trace => ./ require ( github.com/google/go-cmp v0.5.7 github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v1.3.0 + go.opentelemetry.io/otel v1.4.1 ) replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough diff --git a/trace/tracestate.go b/trace/tracestate.go index 9fad16fd277..7b7af6955f9 100644 --- a/trace/tracestate.go +++ b/trace/tracestate.go @@ -58,7 +58,7 @@ func newMember(key, value string) (member, error) { return member{Key: key, Value: value}, nil } -func parseMemeber(m string) (member, error) { +func parseMember(m string) (member, error) { matches := memberRe.FindStringSubmatch(m) if len(matches) != 5 { return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) @@ -114,7 +114,7 @@ func ParseTraceState(tracestate string) (TraceState, error) { continue } - m, err := parseMemeber(memberStr) + m, err := parseMember(memberStr) if err != nil { return TraceState{}, wrapErr(err) } @@ -171,7 +171,8 @@ func (ts TraceState) Get(key string) string { // 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. +// then is allowed, the new list-member will be inserted and the right-most +// list-member will be dropped in the returned TraceState. func (ts TraceState) Insert(key, value string) (TraceState, error) { m, err := newMember(key, value) if err != nil { @@ -179,17 +180,10 @@ func (ts TraceState) Insert(key, value string) (TraceState, error) { } 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) + if cTS.Len()+1 <= maxListMembers { + cTS.list = append(cTS.list, member{}) } - - cTS.list = append(cTS.list, member{}) + // When the number of members exceeds capacity, drop the "right-most". copy(cTS.list[1:], cTS.list) cTS.list[0] = m diff --git a/trace/tracestate_test.go b/trace/tracestate_test.go index edb89073de8..784c1589219 100644 --- a/trace/tracestate_test.go +++ b/trace/tracestate_test.go @@ -26,74 +26,91 @@ import ( // Taken from the W3C tests: // https://github.com/w3c/trace-context/blob/dcd3ad9b7d6ac36f70ff3739874b73c11b0302a1/test/test_data.json var testcases = []struct { + name string in string tracestate TraceState out string err error }{ { - in: "foo=1,foo=1", - err: errDuplicate, + name: "duplicate with the same value", + in: "foo=1,foo=1", + err: errDuplicate, }, { - in: "foo=1,foo=2", - err: errDuplicate, + name: "duplicate with different values", + in: "foo=1,foo=2", + err: errDuplicate, }, { - in: "foo =1", - err: errInvalidMember, + name: "improperly formatted key/value pair", + in: "foo =1", + err: errInvalidMember, }, { - in: "FOO=1", - err: errInvalidMember, + name: "upper case key", + in: "FOO=1", + err: errInvalidMember, }, { - in: "foo.bar=1", - err: errInvalidMember, + name: "key with invalid character", + in: "foo.bar=1", + err: errInvalidMember, }, { - in: "foo@=1,bar=2", - err: errInvalidMember, + name: "multiple keys, one with empty tenant key", + in: "foo@=1,bar=2", + err: errInvalidMember, }, { - in: "@foo=1,bar=2", - err: errInvalidMember, + name: "multiple keys, one with only tenant", + in: "@foo=1,bar=2", + err: errInvalidMember, }, { - in: "foo@@bar=1,bar=2", - err: errInvalidMember, + name: "multiple keys, one with double tenant separator", + in: "foo@@bar=1,bar=2", + err: errInvalidMember, }, { - in: "foo@bar@baz=1,bar=2", - err: errInvalidMember, + name: "multiple keys, one with multiple tenants", + in: "foo@bar@baz=1,bar=2", + err: errInvalidMember, }, { - in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", - err: errInvalidMember, + name: "key too long", + in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", + err: errInvalidMember, }, { - in: "foo=1,tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@v=1", - err: errInvalidMember, + name: "key too long, with tenant", + in: "foo=1,tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@v=1", + err: errInvalidMember, }, { - in: "foo=1,t@vvvvvvvvvvvvvvv=1", - err: errInvalidMember, + name: "tenant too long", + in: "foo=1,t@vvvvvvvvvvvvvvv=1", + err: errInvalidMember, }, { - in: "foo=bar=baz", - err: errInvalidMember, + name: "multiple values for a single key", + in: "foo=bar=baz", + err: errInvalidMember, }, { - in: "foo=,bar=3", - err: errInvalidMember, + name: "no value", + 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, + name: "too many members", + 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{|}~", + name: "valid key/value list", + in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", tracestate: TraceState{list: []member{ { Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/", @@ -102,8 +119,9 @@ var testcases = []struct { }}, }, { - in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", - out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + name: "valid key/value list with tenant", + 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_-*/", @@ -112,35 +130,40 @@ var testcases = []struct { }}, }, { + name: "empty input", // 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", + name: "single key and value", + in: "foo=1", + out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { - in: "foo=1,", - out: "foo=1", + name: "single key and value with empty separator", + in: "foo=1,", + out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { - in: "foo=1,bar=2", - out: "foo=1,bar=2", + name: "multiple keys and values", + 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", + name: "with a key at maximum length", + in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", + out: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1", tracestate: TraceState{list: []member{ { Key: "foo", @@ -153,8 +176,9 @@ var testcases = []struct { }}, }, { - in: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", - out: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", + name: "with a key and tenant at maximum length", + in: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", + out: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1", tracestate: TraceState{list: []member{ { Key: "foo", @@ -167,8 +191,9 @@ var testcases = []struct { }}, }, { - 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", + name: "with maximum members", + 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"}, @@ -205,8 +230,9 @@ var testcases = []struct { }}, }, { - in: "foo=1,bar=2,rojo=1,congo=2,baz=3", - out: "foo=1,bar=2,rojo=1,congo=2,baz=3", + name: "with several members", + 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"}, @@ -216,8 +242,9 @@ var testcases = []struct { }}, }, { - in: "foo=1 \t , \t bar=2, \t baz=3", - out: "foo=1,bar=2,baz=3", + name: "with tabs between members", + 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"}, @@ -225,8 +252,9 @@ var testcases = []struct { }}, }, { - in: "foo=1\t \t,\t \tbar=2,\t \tbaz=3", - out: "foo=1,bar=2,baz=3", + name: "with multiple tabs between members", + 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"}, @@ -234,22 +262,25 @@ var testcases = []struct { }}, }, { - in: "foo=1 ", - out: "foo=1", + name: "with space at the end of the member", + in: "foo=1 ", + out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { - in: "foo=1\t", - out: "foo=1", + name: "with tab at the end of the member", + in: "foo=1\t", + out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, }, { - in: "foo=1 \t", - out: "foo=1", + name: "with tab and space at the end of the member", + in: "foo=1 \t", + out: "foo=1", tracestate: TraceState{list: []member{ {Key: "foo", Value: "1"}, }}, @@ -269,13 +300,15 @@ var maxMembers = func() TraceState { 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) - } + t.Run(tc.name, func(t *testing.T) { + 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) + } + }) } } @@ -285,8 +318,9 @@ func TestTraceStateString(t *testing.T) { // Only test non-zero value TraceState. continue } - - assert.Equal(t, tc.out, tc.tracestate.String()) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.out, tc.tracestate.String()) + }) } } @@ -296,15 +330,16 @@ func TestTraceStateMarshalJSON(t *testing.T) { // Only test non-zero value TraceState. continue } + t.Run(tc.name, func(t *testing.T) { + // Encode UTF-8. + expected, err := json.Marshal(tc.out) + require.NoError(t, err) - // Encode UTF-8. - expected, err := json.Marshal(tc.out) - require.NoError(t, err) - - actual, err := json.Marshal(tc.tracestate) - require.NoError(t, err) + actual, err := json.Marshal(tc.tracestate) + require.NoError(t, err) - assert.Equal(t, expected, actual) + assert.Equal(t, expected, actual) + }) } } @@ -332,7 +367,9 @@ func TestTraceStateGet(t *testing.T) { } for _, tc := range testCases { - assert.Equal(t, tc.expected, maxMembers.Get(tc.key), tc.name) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, maxMembers.Get(tc.key)) + }) } } @@ -377,7 +414,9 @@ func TestTraceStateDelete(t *testing.T) { } for _, tc := range testCases { - assert.Equal(t, tc.expected, ts.Delete(tc.key), tc.name) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, ts.Delete(tc.key)) + }) } } @@ -443,23 +482,31 @@ func TestTraceStateInsert(t *testing.T) { err: errInvalidKey, }, { - name: "too many entries", + name: "drop the right-most member(oldest) in queue", tracestate: maxMembers, key: "keyx", value: "valx", - expected: maxMembers, - err: errMemberNumber, - }, - } + expected: func() TraceState { + // Prepend the new element and remove the oldest one, which is over capacity. + return TraceState{ + list: append( + []member{{Key: "keyx", Value: "valx"}}, + maxMembers.list[:len(maxMembers.list)-1]..., + ), + } + }(), + }} 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) - } + t.Run(tc.name, func(t *testing.T) { + 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) + } else { + assert.Equal(t, tc.expected, actual) + } + }) } } diff --git a/version.go b/version.go index e62acd66e7b..a09bcbb5e8f 100644 --- a/version.go +++ b/version.go @@ -16,5 +16,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.3.0" + return "1.4.1" } diff --git a/versions.yaml b/versions.yaml index 3e13849987d..3f06f299346 100644 --- a/versions.yaml +++ b/versions.yaml @@ -14,7 +14,7 @@ module-sets: stable-v1: - version: v1.3.0 + version: v1.4.1 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opentracing @@ -34,7 +34,7 @@ module-sets: - go.opentelemetry.io/otel/trace - go.opentelemetry.io/otel/sdk experimental-metrics: - version: v0.26.0 + version: v0.27.0 modules: - go.opentelemetry.io/otel/example/prometheus - go.opentelemetry.io/otel/exporters/otlp/otlpmetric @@ -47,11 +47,11 @@ module-sets: - go.opentelemetry.io/otel/sdk/export/metric - go.opentelemetry.io/otel/sdk/metric experimental-schema: - version: v0.0.1 + version: v0.0.2 modules: - go.opentelemetry.io/otel/schema bridge: - version: v0.26.0 + version: v0.27.1 modules: - go.opentelemetry.io/otel/bridge/opencensus - go.opentelemetry.io/otel/bridge/opencensus/test diff --git a/website_docs/libraries.md b/website_docs/libraries.md index 064af7ba0fc..8cd772da06d 100644 --- a/website_docs/libraries.md +++ b/website_docs/libraries.md @@ -92,4 +92,4 @@ A full list of instrumentation libraries available can be found in the [OpenTele Instrumentation libraries can do things like generate telemetry data for inbound and outbound HTTP requests, but they don't instrument your actual application. -To get richer telemetry data, use [manual instrumentatiion]({{< relref "manual" >}}) to enrich your telemetry data from instrumentation libraries with instrumentation from your running application. +To get richer telemetry data, use [manual instrumentation]({{< relref "manual" >}}) to enrich your telemetry data from instrumentation libraries with instrumentation from your running application.