Merge branch 'main' into main
This commit is contained in:
commit
513a0d4efc
|
|
@ -32,13 +32,13 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/init@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3
|
||||
with:
|
||||
languages: go
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/autobuild@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/analyze@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ name: Markdown (Fail Fast)
|
|||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "**.md"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.md"
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changedfiles:
|
||||
|
|
@ -17,11 +21,34 @@ jobs:
|
|||
- name: Checkout Repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Shallow clone, but enough for `git diff HEAD~1 HEAD`.
|
||||
fetch-depth: 2
|
||||
- name: Get changed files
|
||||
id: changes
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha || '' }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "md=$(git diff --name-only --diff-filter=ACMRTUXB origin/${{ github.event.pull_request.base.ref }} ${{ github.event.pull_request.head.sha }} | grep .md$ | xargs)" >> $GITHUB_OUTPUT
|
||||
echo "Detecting changed markdown files..."
|
||||
|
||||
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
||||
echo "Running in pull_request context"
|
||||
echo "Base SHA: $BASE_SHA"
|
||||
echo "Head SHA: $HEAD_SHA"
|
||||
CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep '\.md$' || true)
|
||||
elif [[ "$EVENT_NAME" == "push" ]]; then
|
||||
echo "Running in push context"
|
||||
CHANGED=$(git diff --name-only HEAD~1 HEAD | grep '\.md$' || true)
|
||||
else
|
||||
echo "Unsupported event type: $EVENT_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MD=$(echo "$CHANGED" | tr '\n' ' ' | xargs)
|
||||
echo "Markdown files changed: $MD"
|
||||
echo "md=$MD" >> "$GITHUB_OUTPUT"
|
||||
|
||||
lint:
|
||||
name: lint markdown files
|
||||
|
|
@ -29,9 +56,9 @@ jobs:
|
|||
needs: changedfiles
|
||||
if: ${{needs.changedfiles.outputs.md}}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Run linter
|
||||
uses: docker://avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee
|
||||
with:
|
||||
args: ${{needs.changedfiles.outputs.md}}
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Run linter
|
||||
uses: docker://avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee
|
||||
with:
|
||||
args: ${{needs.changedfiles.outputs.md}}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,6 @@ jobs:
|
|||
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/upload-sarif@d6bbdef45e766d081b84a2def353b0055f728d3e # v3.29.3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
|||
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -46,6 +46,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
- The `go.opentelemetry.io/otel/semconv/v1.36.0` package.
|
||||
The package contains semantic conventions from the `v1.36.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.36.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.34.0.`(#7032)
|
||||
- Add experimental self-observability span metrics in `go.opentelemetry.io/otel/sdk/trace`.
|
||||
Check the `go.opentelemetry.io/otel/sdk/trace/internal/x` package documentation for more information. (#7027)
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
@ -55,6 +57,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [0.59.1] 2025-07-21
|
||||
|
||||
### Changed
|
||||
|
||||
- Retract `v0.59.0` release of `go.opentelemetry.io/otel/exporters/prometheus` module which appends incorrect unit suffixes. (#7046)
|
||||
- Change `go.opentelemetry.io/otel/exporters/prometheus` to no longer deduplicate suffixes when UTF8 is enabled.
|
||||
It is recommended to disable unit and counter suffixes in the exporter, and manually add suffixes if you rely on the existing behavior. (#7044)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `go.opentelemetry.io/otel/exporters/prometheus` to properly handle unit suffixes when the unit is in brackets.
|
||||
E.g. `{spans}`. (#7044)
|
||||
|
||||
## [1.37.0/0.59.0/0.13.0] 2025-06-25
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# This is a renovate-friendly source of Docker images.
|
||||
FROM python:3.13.5-slim-bullseye@sha256:631af3fee9d0b0a046855a62af745c1f94b75c5309be8802a0928cce3ac0f98d AS python
|
||||
FROM python:3.13.5-slim-bullseye@sha256:17c88fd024ef9506f1ceb6ecdec639c3245d97904128e195ec03022fca40ce4c AS python
|
||||
FROM otel/weaver:v0.16.1@sha256:5ca4901b460217604ddb83feaca05238e2b016a226ecfb9b87a95555918a03af AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ require (
|
|||
go.opentelemetry.io/otel/sdk/log/logtest v0.13.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
go.opentelemetry.io/proto/otlp v1.7.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
|
@ -33,7 +33,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
@ -50,3 +50,5 @@ replace go.opentelemetry.io/otel/trace => ../../../../trace
|
|||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/log/logtest => ../../../../sdk/log/logtest
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
|
|
@ -37,10 +35,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
@ -50,3 +50,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
|||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/log => ../../../../log
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
|
|
@ -37,10 +35,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ require (
|
|||
go.opentelemetry.io/otel/sdk v1.37.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0
|
||||
go.opentelemetry.io/proto/otlp v1.7.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
|
@ -30,7 +30,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -31,3 +31,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../sdk
|
|||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
go.opentelemetry.io/proto/otlp v1.7.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
|
@ -28,7 +28,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
@ -41,3 +41,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../
|
|||
replace go.opentelemetry.io/otel/trace => ../../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
|
@ -39,10 +37,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
@ -40,3 +40,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk
|
|||
replace go.opentelemetry.io/otel/trace => ../../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
|
@ -39,10 +37,10 @@ golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
|||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79 h1:iOye66xuaAK0WnkPuhQPUFy8eJcmwUXqGGP3om6IxX8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250715232539-7130f93afb79/go.mod h1:HKJDgKsFUnv5VAGeQjz8kxcgDP0HoE0iZNp0OdZNlhE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@
|
|||
package prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
|
|
@ -139,17 +137,6 @@ func WithoutScopeInfo() Option {
|
|||
// have special behavior based on their name.
|
||||
func WithNamespace(ns string) Option {
|
||||
return optionFunc(func(cfg config) config {
|
||||
if model.NameValidationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
|
||||
logDeprecatedLegacyScheme()
|
||||
// Only sanitize if prometheus does not support UTF-8.
|
||||
ns = model.EscapeName(ns, model.NameEscapingScheme)
|
||||
}
|
||||
if !strings.HasSuffix(ns, "_") {
|
||||
// namespace and metric names should be separated with an underscore,
|
||||
// adds a trailing underscore if there is not one already.
|
||||
ns = ns + "_"
|
||||
}
|
||||
|
||||
cfg.namespace = ns
|
||||
return cfg
|
||||
})
|
||||
|
|
|
|||
|
|
@ -113,17 +113,17 @@ func TestNewConfig(t *testing.T) {
|
|||
},
|
||||
wantConfig: config{
|
||||
registerer: prometheus.DefaultRegisterer,
|
||||
namespace: "test_",
|
||||
namespace: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with namespace with trailing underscore",
|
||||
options: []Option{
|
||||
WithNamespace("test_"),
|
||||
WithNamespace("test"),
|
||||
},
|
||||
wantConfig: config{
|
||||
registerer: prometheus.DefaultRegisterer,
|
||||
namespace: "test_",
|
||||
namespace: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -133,7 +133,7 @@ func TestNewConfig(t *testing.T) {
|
|||
},
|
||||
wantConfig: config{
|
||||
registerer: prometheus.DefaultRegisterer,
|
||||
namespace: "test/_",
|
||||
namespace: "test/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/otlptranslator"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
|
|
@ -27,16 +28,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
targetInfoMetricName = "target_info"
|
||||
targetInfoDescription = "Target metadata"
|
||||
|
||||
scopeLabelPrefix = "otel_scope_"
|
||||
scopeNameLabel = scopeLabelPrefix + "name"
|
||||
scopeVersionLabel = scopeLabelPrefix + "version"
|
||||
scopeSchemaLabel = scopeLabelPrefix + "schema_url"
|
||||
|
||||
traceIDExemplarKey = "trace_id"
|
||||
spanIDExemplarKey = "span_id"
|
||||
)
|
||||
|
||||
var metricsPool = sync.Pool{
|
||||
|
|
@ -93,12 +90,11 @@ type collector struct {
|
|||
targetInfo prometheus.Metric
|
||||
metricFamilies map[string]*dto.MetricFamily
|
||||
resourceKeyVals keyVals
|
||||
metricNamer otlptranslator.MetricNamer
|
||||
labelNamer otlptranslator.LabelNamer
|
||||
unitNamer otlptranslator.UnitNamer
|
||||
}
|
||||
|
||||
// prometheus counters MUST have a _total suffix by default:
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/compatibility/prometheus_and_openmetrics.md
|
||||
const counterSuffix = "total"
|
||||
|
||||
// New returns a Prometheus Exporter.
|
||||
func New(opts ...Option) (*Exporter, error) {
|
||||
cfg := newConfig(opts...)
|
||||
|
|
@ -108,6 +104,12 @@ func New(opts ...Option) (*Exporter, error) {
|
|||
// TODO (#3244): Enable some way to configure the reader, but not change temporality.
|
||||
reader := metric.NewManualReader(cfg.readerOpts...)
|
||||
|
||||
utf8Allowed := model.NameValidationScheme == model.UTF8Validation // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
|
||||
if !utf8Allowed {
|
||||
// Only sanitize if prometheus does not support UTF-8.
|
||||
logDeprecatedLegacyScheme()
|
||||
}
|
||||
labelNamer := otlptranslator.LabelNamer{UTF8Allowed: utf8Allowed}
|
||||
collector := &collector{
|
||||
reader: reader,
|
||||
disableTargetInfo: cfg.disableTargetInfo,
|
||||
|
|
@ -115,8 +117,18 @@ func New(opts ...Option) (*Exporter, error) {
|
|||
withoutCounterSuffixes: cfg.withoutCounterSuffixes,
|
||||
disableScopeInfo: cfg.disableScopeInfo,
|
||||
metricFamilies: make(map[string]*dto.MetricFamily),
|
||||
namespace: cfg.namespace,
|
||||
namespace: labelNamer.Build(cfg.namespace),
|
||||
resourceAttributesFilter: cfg.resourceAttributesFilter,
|
||||
metricNamer: otlptranslator.MetricNamer{
|
||||
Namespace: cfg.namespace,
|
||||
// We decide whether to pass type and unit to the netricNamer based
|
||||
// on whether units or counter suffixes are enabled, and keep this
|
||||
// always enabled.
|
||||
WithMetricSuffixes: true,
|
||||
UTF8Allowed: utf8Allowed,
|
||||
},
|
||||
unitNamer: otlptranslator.UnitNamer{UTF8Allowed: utf8Allowed},
|
||||
labelNamer: labelNamer,
|
||||
}
|
||||
|
||||
if err := cfg.registerer.Register(collector); err != nil {
|
||||
|
|
@ -164,7 +176,11 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
|||
defer c.mu.Unlock()
|
||||
|
||||
if c.targetInfo == nil && !c.disableTargetInfo {
|
||||
targetInfo, err := createInfoMetric(targetInfoMetricName, targetInfoDescription, metrics.Resource)
|
||||
targetInfo, err := c.createInfoMetric(
|
||||
otlptranslator.TargetInfoMetricName,
|
||||
targetInfoDescription,
|
||||
metrics.Resource,
|
||||
)
|
||||
if err != nil {
|
||||
// If the target info metric is invalid, disable sending it.
|
||||
c.disableTargetInfo = true
|
||||
|
|
@ -195,7 +211,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
|||
kv.keys = append(kv.keys, scopeNameLabel, scopeVersionLabel, scopeSchemaLabel)
|
||||
kv.vals = append(kv.vals, scopeMetrics.Scope.Name, scopeMetrics.Scope.Version, scopeMetrics.Scope.SchemaURL)
|
||||
|
||||
attrKeys, attrVals := getAttrs(scopeMetrics.Scope.Attributes)
|
||||
attrKeys, attrVals := getAttrs(scopeMetrics.Scope.Attributes, c.labelNamer)
|
||||
for i := range attrKeys {
|
||||
attrKeys[i] = scopeLabelPrefix + attrKeys[i]
|
||||
}
|
||||
|
|
@ -211,7 +227,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
|||
if typ == nil {
|
||||
continue
|
||||
}
|
||||
name := c.getName(m, typ)
|
||||
name := c.getName(m)
|
||||
|
||||
drop, help := c.validateMetrics(name, m.Description, typ)
|
||||
if drop {
|
||||
|
|
@ -224,21 +240,21 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
|||
|
||||
switch v := m.Data.(type) {
|
||||
case metricdata.Histogram[int64]:
|
||||
addHistogramMetric(ch, v, m, name, kv)
|
||||
addHistogramMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.Histogram[float64]:
|
||||
addHistogramMetric(ch, v, m, name, kv)
|
||||
addHistogramMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
addExponentialHistogramMetric(ch, v, m, name, kv)
|
||||
addExponentialHistogramMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
addExponentialHistogramMetric(ch, v, m, name, kv)
|
||||
addExponentialHistogramMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.Sum[int64]:
|
||||
addSumMetric(ch, v, m, name, kv)
|
||||
addSumMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.Sum[float64]:
|
||||
addSumMetric(ch, v, m, name, kv)
|
||||
addSumMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.Gauge[int64]:
|
||||
addGaugeMetric(ch, v, m, name, kv)
|
||||
addGaugeMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
case metricdata.Gauge[float64]:
|
||||
addGaugeMetric(ch, v, m, name, kv)
|
||||
addGaugeMetric(ch, v, m, name, kv, c.labelNamer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -303,9 +319,10 @@ func addExponentialHistogramMetric[N int64 | float64](
|
|||
m metricdata.Metrics,
|
||||
name string,
|
||||
kv keyVals,
|
||||
labelNamer otlptranslator.LabelNamer,
|
||||
) {
|
||||
for _, dp := range histogram.DataPoints {
|
||||
keys, values := getAttrs(dp.Attributes)
|
||||
keys, values := getAttrs(dp.Attributes, labelNamer)
|
||||
keys = append(keys, kv.keys...)
|
||||
values = append(values, kv.vals...)
|
||||
|
||||
|
|
@ -377,9 +394,10 @@ func addHistogramMetric[N int64 | float64](
|
|||
m metricdata.Metrics,
|
||||
name string,
|
||||
kv keyVals,
|
||||
labelNamer otlptranslator.LabelNamer,
|
||||
) {
|
||||
for _, dp := range histogram.DataPoints {
|
||||
keys, values := getAttrs(dp.Attributes)
|
||||
keys, values := getAttrs(dp.Attributes, labelNamer)
|
||||
keys = append(keys, kv.keys...)
|
||||
values = append(values, kv.vals...)
|
||||
|
||||
|
|
@ -396,7 +414,7 @@ func addHistogramMetric[N int64 | float64](
|
|||
otel.Handle(err)
|
||||
continue
|
||||
}
|
||||
m = addExemplars(m, dp.Exemplars)
|
||||
m = addExemplars(m, dp.Exemplars, labelNamer)
|
||||
ch <- m
|
||||
}
|
||||
}
|
||||
|
|
@ -407,6 +425,7 @@ func addSumMetric[N int64 | float64](
|
|||
m metricdata.Metrics,
|
||||
name string,
|
||||
kv keyVals,
|
||||
labelNamer otlptranslator.LabelNamer,
|
||||
) {
|
||||
valueType := prometheus.CounterValue
|
||||
if !sum.IsMonotonic {
|
||||
|
|
@ -414,7 +433,7 @@ func addSumMetric[N int64 | float64](
|
|||
}
|
||||
|
||||
for _, dp := range sum.DataPoints {
|
||||
keys, values := getAttrs(dp.Attributes)
|
||||
keys, values := getAttrs(dp.Attributes, labelNamer)
|
||||
keys = append(keys, kv.keys...)
|
||||
values = append(values, kv.vals...)
|
||||
|
||||
|
|
@ -427,7 +446,7 @@ func addSumMetric[N int64 | float64](
|
|||
// GaugeValues don't support Exemplars at this time
|
||||
// https://github.com/prometheus/client_golang/blob/aef8aedb4b6e1fb8ac1c90790645169125594096/prometheus/metric.go#L199
|
||||
if valueType != prometheus.GaugeValue {
|
||||
m = addExemplars(m, dp.Exemplars)
|
||||
m = addExemplars(m, dp.Exemplars, labelNamer)
|
||||
}
|
||||
ch <- m
|
||||
}
|
||||
|
|
@ -439,9 +458,10 @@ func addGaugeMetric[N int64 | float64](
|
|||
m metricdata.Metrics,
|
||||
name string,
|
||||
kv keyVals,
|
||||
labelNamer otlptranslator.LabelNamer,
|
||||
) {
|
||||
for _, dp := range gauge.DataPoints {
|
||||
keys, values := getAttrs(dp.Attributes)
|
||||
keys, values := getAttrs(dp.Attributes, labelNamer)
|
||||
keys = append(keys, kv.keys...)
|
||||
values = append(values, kv.vals...)
|
||||
|
||||
|
|
@ -457,12 +477,12 @@ func addGaugeMetric[N int64 | float64](
|
|||
|
||||
// getAttrs converts the attribute.Set to two lists of matching Prometheus-style
|
||||
// keys and values.
|
||||
func getAttrs(attrs attribute.Set) ([]string, []string) {
|
||||
func getAttrs(attrs attribute.Set, labelNamer otlptranslator.LabelNamer) ([]string, []string) {
|
||||
keys := make([]string, 0, attrs.Len())
|
||||
values := make([]string, 0, attrs.Len())
|
||||
itr := attrs.Iter()
|
||||
|
||||
if model.NameValidationScheme == model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
|
||||
if labelNamer.UTF8Allowed {
|
||||
// Do not perform sanitization if prometheus supports UTF-8.
|
||||
for itr.Next() {
|
||||
kv := itr.Attribute()
|
||||
|
|
@ -475,7 +495,7 @@ func getAttrs(attrs attribute.Set) ([]string, []string) {
|
|||
keysMap := make(map[string][]string)
|
||||
for itr.Next() {
|
||||
kv := itr.Attribute()
|
||||
key := model.EscapeName(string(kv.Key), model.NameEscapingScheme)
|
||||
key := labelNamer.Build(string(kv.Key))
|
||||
if _, ok := keysMap[key]; !ok {
|
||||
keysMap[key] = []string{kv.Value.Emit()}
|
||||
} else {
|
||||
|
|
@ -492,91 +512,22 @@ func getAttrs(attrs attribute.Set) ([]string, []string) {
|
|||
return keys, values
|
||||
}
|
||||
|
||||
func createInfoMetric(name, description string, res *resource.Resource) (prometheus.Metric, error) {
|
||||
keys, values := getAttrs(*res.Set())
|
||||
func (c *collector) createInfoMetric(name, description string, res *resource.Resource) (prometheus.Metric, error) {
|
||||
keys, values := getAttrs(*res.Set(), c.labelNamer)
|
||||
desc := prometheus.NewDesc(name, description, keys, nil)
|
||||
return prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(1), values...)
|
||||
}
|
||||
|
||||
func unitMapGetOrDefault(unit string) string {
|
||||
if promUnit, ok := unitSuffixes[unit]; ok {
|
||||
return promUnit
|
||||
}
|
||||
return unit
|
||||
}
|
||||
|
||||
var unitSuffixes = map[string]string{
|
||||
// Time
|
||||
"d": "days",
|
||||
"h": "hours",
|
||||
"min": "minutes",
|
||||
"s": "seconds",
|
||||
"ms": "milliseconds",
|
||||
"us": "microseconds",
|
||||
"ns": "nanoseconds",
|
||||
|
||||
// Bytes
|
||||
"By": "bytes",
|
||||
"KiBy": "kibibytes",
|
||||
"MiBy": "mebibytes",
|
||||
"GiBy": "gibibytes",
|
||||
"TiBy": "tibibytes",
|
||||
"KBy": "kilobytes",
|
||||
"MBy": "megabytes",
|
||||
"GBy": "gigabytes",
|
||||
"TBy": "terabytes",
|
||||
|
||||
// SI
|
||||
"m": "meters",
|
||||
"V": "volts",
|
||||
"A": "amperes",
|
||||
"J": "joules",
|
||||
"W": "watts",
|
||||
"g": "grams",
|
||||
|
||||
// Misc
|
||||
"Cel": "celsius",
|
||||
"Hz": "hertz",
|
||||
"1": "ratio",
|
||||
"%": "percent",
|
||||
}
|
||||
|
||||
// getName returns the sanitized name, prefixed with the namespace and suffixed with unit.
|
||||
func (c *collector) getName(m metricdata.Metrics, typ *dto.MetricType) string {
|
||||
name := m.Name
|
||||
if model.NameValidationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
|
||||
// Only sanitize if prometheus does not support UTF-8.
|
||||
logDeprecatedLegacyScheme()
|
||||
name = model.EscapeName(name, model.NameEscapingScheme)
|
||||
func (c *collector) getName(m metricdata.Metrics) string {
|
||||
translatorMetric := otlptranslator.Metric{
|
||||
Name: m.Name,
|
||||
Type: c.namingMetricType(m),
|
||||
}
|
||||
addCounterSuffix := !c.withoutCounterSuffixes && *typ == dto.MetricType_COUNTER
|
||||
if addCounterSuffix {
|
||||
// Remove the _total suffix here, as we will re-add the total suffix
|
||||
// later, and it needs to come after the unit suffix.
|
||||
name = strings.TrimSuffix(name, counterSuffix)
|
||||
// If the last character is an underscore, or would be converted to an underscore, trim it from the name.
|
||||
// an underscore will be added back in later.
|
||||
if convertsToUnderscore(rune(name[len(name)-1])) {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
if !c.withoutUnits {
|
||||
translatorMetric.Unit = m.Unit
|
||||
}
|
||||
if c.namespace != "" {
|
||||
name = c.namespace + name
|
||||
}
|
||||
if suffix := unitMapGetOrDefault(m.Unit); suffix != "" && !c.withoutUnits && !strings.HasSuffix(name, suffix) {
|
||||
name += "_" + suffix
|
||||
}
|
||||
if addCounterSuffix {
|
||||
name += "_" + counterSuffix
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// convertsToUnderscore returns true if the character would be converted to an
|
||||
// underscore when the escaping scheme is underscore escaping. This is meant to
|
||||
// capture any character that should be considered a "delimiter".
|
||||
func convertsToUnderscore(b rune) bool {
|
||||
return (b < 'a' || b > 'z') && (b < 'A' || b > 'Z') && b != ':' && (b < '0' || b > '9')
|
||||
return c.metricNamer.Build(translatorMetric)
|
||||
}
|
||||
|
||||
func (c *collector) metricType(m metricdata.Metrics) *dto.MetricType {
|
||||
|
|
@ -601,12 +552,41 @@ func (c *collector) metricType(m metricdata.Metrics) *dto.MetricType {
|
|||
return nil
|
||||
}
|
||||
|
||||
// namingMetricType provides the metric type for naming purposes.
|
||||
func (c *collector) namingMetricType(m metricdata.Metrics) otlptranslator.MetricType {
|
||||
switch v := m.Data.(type) {
|
||||
case metricdata.ExponentialHistogram[int64], metricdata.ExponentialHistogram[float64]:
|
||||
return otlptranslator.MetricTypeHistogram
|
||||
case metricdata.Histogram[int64], metricdata.Histogram[float64]:
|
||||
return otlptranslator.MetricTypeHistogram
|
||||
case metricdata.Sum[float64]:
|
||||
// If counter suffixes are disabled, treat them like non-monotonic
|
||||
// suffixes for the purposes of naming.
|
||||
if v.IsMonotonic && !c.withoutCounterSuffixes {
|
||||
return otlptranslator.MetricTypeMonotonicCounter
|
||||
}
|
||||
return otlptranslator.MetricTypeNonMonotonicCounter
|
||||
case metricdata.Sum[int64]:
|
||||
// If counter suffixes are disabled, treat them like non-monotonic
|
||||
// suffixes for the purposes of naming.
|
||||
if v.IsMonotonic && !c.withoutCounterSuffixes {
|
||||
return otlptranslator.MetricTypeMonotonicCounter
|
||||
}
|
||||
return otlptranslator.MetricTypeNonMonotonicCounter
|
||||
case metricdata.Gauge[int64], metricdata.Gauge[float64]:
|
||||
return otlptranslator.MetricTypeGauge
|
||||
case metricdata.Summary:
|
||||
return otlptranslator.MetricTypeSummary
|
||||
}
|
||||
return otlptranslator.MetricTypeUnknown
|
||||
}
|
||||
|
||||
func (c *collector) createResourceAttributes(res *resource.Resource) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
resourceAttrs, _ := res.Set().Filter(c.resourceAttributesFilter)
|
||||
resourceKeys, resourceValues := getAttrs(resourceAttrs)
|
||||
resourceKeys, resourceValues := getAttrs(resourceAttrs, c.labelNamer)
|
||||
c.resourceKeyVals = keyVals{keys: resourceKeys, vals: resourceValues}
|
||||
}
|
||||
|
||||
|
|
@ -648,16 +628,20 @@ func (c *collector) validateMetrics(name, description string, metricType *dto.Me
|
|||
return false, ""
|
||||
}
|
||||
|
||||
func addExemplars[N int64 | float64](m prometheus.Metric, exemplars []metricdata.Exemplar[N]) prometheus.Metric {
|
||||
func addExemplars[N int64 | float64](
|
||||
m prometheus.Metric,
|
||||
exemplars []metricdata.Exemplar[N],
|
||||
labelNamer otlptranslator.LabelNamer,
|
||||
) prometheus.Metric {
|
||||
if len(exemplars) == 0 {
|
||||
return m
|
||||
}
|
||||
promExemplars := make([]prometheus.Exemplar, len(exemplars))
|
||||
for i, exemplar := range exemplars {
|
||||
labels := attributesToLabels(exemplar.FilteredAttributes)
|
||||
labels := attributesToLabels(exemplar.FilteredAttributes, labelNamer)
|
||||
// Overwrite any existing trace ID or span ID attributes
|
||||
labels[traceIDExemplarKey] = hex.EncodeToString(exemplar.TraceID[:])
|
||||
labels[spanIDExemplarKey] = hex.EncodeToString(exemplar.SpanID[:])
|
||||
labels[otlptranslator.ExemplarTraceIDKey] = hex.EncodeToString(exemplar.TraceID[:])
|
||||
labels[otlptranslator.ExemplarSpanIDKey] = hex.EncodeToString(exemplar.SpanID[:])
|
||||
promExemplars[i] = prometheus.Exemplar{
|
||||
Value: float64(exemplar.Value),
|
||||
Timestamp: exemplar.Time,
|
||||
|
|
@ -674,11 +658,10 @@ func addExemplars[N int64 | float64](m prometheus.Metric, exemplars []metricdata
|
|||
return metricWithExemplar
|
||||
}
|
||||
|
||||
func attributesToLabels(attrs []attribute.KeyValue) prometheus.Labels {
|
||||
func attributesToLabels(attrs []attribute.KeyValue, labelNamer otlptranslator.LabelNamer) prometheus.Labels {
|
||||
labels := make(map[string]string)
|
||||
for _, attr := range attrs {
|
||||
key := model.EscapeName(string(attr.Key), model.NameEscapingScheme)
|
||||
labels[key] = attr.Value.Emit()
|
||||
labels[labelNamer.Build(string(attr.Key))] = attr.Value.Emit()
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/otlptranslator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ func TestPrometheusExporter(t *testing.T) {
|
|||
{
|
||||
name: "counter",
|
||||
expectedFile: "testdata/counter.txt",
|
||||
disableUTF8: true,
|
||||
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
|
||||
opt := otelmetric.WithAttributes(
|
||||
attribute.Key("A").String("B"),
|
||||
|
|
@ -71,7 +73,8 @@ func TestPrometheusExporter(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "counter that already has the unit suffix",
|
||||
expectedFile: "testdata/counter_with_unit_suffix.txt",
|
||||
expectedFile: "testdata/counter_noutf8_with_unit_suffix.txt",
|
||||
disableUTF8: true,
|
||||
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
|
||||
opt := otelmetric.WithAttributes(
|
||||
attribute.Key("A").String("B"),
|
||||
|
|
@ -127,9 +130,39 @@ func TestPrometheusExporter(t *testing.T) {
|
|||
counter.Add(ctx, 5, otelmetric.WithAttributeSet(attrs2))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "counter with bracketed unit",
|
||||
expectedFile: "testdata/counter_no_unit.txt",
|
||||
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
|
||||
opt := otelmetric.WithAttributes(
|
||||
attribute.Key("A").String("B"),
|
||||
attribute.Key("C").String("D"),
|
||||
attribute.Key("E").Bool(true),
|
||||
attribute.Key("F").Int(42),
|
||||
)
|
||||
counter, err := meter.Float64Counter(
|
||||
"foo",
|
||||
otelmetric.WithDescription("a simple counter"),
|
||||
otelmetric.WithUnit("{spans}"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
counter.Add(ctx, 5, opt)
|
||||
counter.Add(ctx, 10.3, opt)
|
||||
counter.Add(ctx, 9, opt)
|
||||
|
||||
attrs2 := attribute.NewSet(
|
||||
attribute.Key("A").String("D"),
|
||||
attribute.Key("C").String("B"),
|
||||
attribute.Key("E").Bool(true),
|
||||
attribute.Key("F").Int(42),
|
||||
)
|
||||
counter.Add(ctx, 5, otelmetric.WithAttributeSet(attrs2))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "counter that already has a total suffix",
|
||||
expectedFile: "testdata/counter.txt",
|
||||
disableUTF8: true,
|
||||
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
|
||||
opt := otelmetric.WithAttributes(
|
||||
attribute.Key("A").String("B"),
|
||||
|
|
@ -194,14 +227,13 @@ func TestPrometheusExporter(t *testing.T) {
|
|||
attribute.Key("A").String("B"),
|
||||
attribute.Key("C").String("D"),
|
||||
)
|
||||
gauge, err := meter.Float64UpDownCounter(
|
||||
gauge, err := meter.Float64Gauge(
|
||||
"bar",
|
||||
otelmetric.WithDescription("a fun little gauge"),
|
||||
otelmetric.WithUnit("1"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
gauge.Add(ctx, 1.0, opt)
|
||||
gauge.Add(ctx, -.25, opt)
|
||||
gauge.Record(ctx, .75, opt)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -398,14 +430,13 @@ func TestPrometheusExporter(t *testing.T) {
|
|||
attribute.Key("A").String("B"),
|
||||
attribute.Key("C").String("D"),
|
||||
)
|
||||
gauge, err := meter.Int64UpDownCounter(
|
||||
gauge, err := meter.Int64Gauge(
|
||||
"bar",
|
||||
otelmetric.WithDescription("a fun little gauge"),
|
||||
otelmetric.WithUnit("1"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
gauge.Add(ctx, 2, opt)
|
||||
gauge.Add(ctx, -1, opt)
|
||||
gauge.Record(ctx, 1, opt)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -1011,20 +1042,20 @@ func TestExemplars(t *testing.T) {
|
|||
attribute.Key("F.4").Int(42),
|
||||
)
|
||||
expectedNonEscapedLabels := map[string]string{
|
||||
traceIDExemplarKey: "01000000000000000000000000000000",
|
||||
spanIDExemplarKey: "0100000000000000",
|
||||
"A.1": "B",
|
||||
"C.2": "D",
|
||||
"E.3": "true",
|
||||
"F.4": "42",
|
||||
otlptranslator.ExemplarTraceIDKey: "01000000000000000000000000000000",
|
||||
otlptranslator.ExemplarSpanIDKey: "0100000000000000",
|
||||
"A.1": "B",
|
||||
"C.2": "D",
|
||||
"E.3": "true",
|
||||
"F.4": "42",
|
||||
}
|
||||
expectedEscapedLabels := map[string]string{
|
||||
traceIDExemplarKey: "01000000000000000000000000000000",
|
||||
spanIDExemplarKey: "0100000000000000",
|
||||
"A_1": "B",
|
||||
"C_2": "D",
|
||||
"E_3": "true",
|
||||
"F_4": "42",
|
||||
otlptranslator.ExemplarTraceIDKey: "01000000000000000000000000000000",
|
||||
otlptranslator.ExemplarSpanIDKey: "0100000000000000",
|
||||
"A_1": "B",
|
||||
"C_2": "D",
|
||||
"E_3": "true",
|
||||
"F_4": "42",
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
|
@ -1241,7 +1272,14 @@ func TestExponentialHistogramScaleValidation(t *testing.T) {
|
|||
Description: "test",
|
||||
}
|
||||
|
||||
addExponentialHistogramMetric(ch, histogram, m, "test_histogram", keyVals{})
|
||||
addExponentialHistogramMetric(
|
||||
ch,
|
||||
histogram,
|
||||
m,
|
||||
"test_histogram",
|
||||
keyVals{},
|
||||
otlptranslator.LabelNamer{},
|
||||
)
|
||||
assert.Error(t, capturedError)
|
||||
assert.Contains(t, capturedError.Error(), "scale -5 is below minimum")
|
||||
select {
|
||||
|
|
@ -1398,7 +1436,14 @@ func TestExponentialHistogramHighScaleDownscaling(t *testing.T) {
|
|||
}
|
||||
|
||||
// This should not produce any errors and should properly downscale buckets
|
||||
addExponentialHistogramMetric(ch, histogram, m, "test_high_scale_histogram", keyVals{})
|
||||
addExponentialHistogramMetric(
|
||||
ch,
|
||||
histogram,
|
||||
m,
|
||||
"test_high_scale_histogram",
|
||||
keyVals{},
|
||||
otlptranslator.LabelNamer{},
|
||||
)
|
||||
|
||||
// Verify a metric was produced
|
||||
select {
|
||||
|
|
@ -1453,7 +1498,14 @@ func TestExponentialHistogramHighScaleDownscaling(t *testing.T) {
|
|||
}
|
||||
|
||||
// This should not produce any errors and should properly downscale buckets
|
||||
addExponentialHistogramMetric(ch, histogram, m, "test_very_high_scale_histogram", keyVals{})
|
||||
addExponentialHistogramMetric(
|
||||
ch,
|
||||
histogram,
|
||||
m,
|
||||
"test_very_high_scale_histogram",
|
||||
keyVals{},
|
||||
otlptranslator.LabelNamer{},
|
||||
)
|
||||
|
||||
// Verify a metric was produced
|
||||
select {
|
||||
|
|
@ -1508,7 +1560,14 @@ func TestExponentialHistogramHighScaleDownscaling(t *testing.T) {
|
|||
}
|
||||
|
||||
// This should handle negative buckets correctly
|
||||
addExponentialHistogramMetric(ch, histogram, m, "test_histogram_with_negative_buckets", keyVals{})
|
||||
addExponentialHistogramMetric(
|
||||
ch,
|
||||
histogram,
|
||||
m,
|
||||
"test_histogram_with_negative_buckets",
|
||||
keyVals{},
|
||||
otlptranslator.LabelNamer{},
|
||||
)
|
||||
|
||||
// Verify a metric was produced
|
||||
select {
|
||||
|
|
@ -1557,7 +1616,14 @@ func TestExponentialHistogramHighScaleDownscaling(t *testing.T) {
|
|||
}
|
||||
|
||||
// This should handle int64 exponential histograms correctly
|
||||
addExponentialHistogramMetric(ch, histogram, m, "test_int64_exponential_histogram", keyVals{})
|
||||
addExponentialHistogramMetric(
|
||||
ch,
|
||||
histogram,
|
||||
m,
|
||||
"test_int64_exponential_histogram",
|
||||
keyVals{},
|
||||
otlptranslator.LabelNamer{},
|
||||
)
|
||||
|
||||
// Verify a metric was produced
|
||||
select {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@ module go.opentelemetry.io/otel/exporters/prometheus
|
|||
|
||||
go 1.23.0
|
||||
|
||||
// v0.59.0 produces incorrect metric names when bracketed units are used.
|
||||
// https://github.com/open-telemetry/opentelemetry-go/issues/7039
|
||||
retract v0.59.0
|
||||
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/otel v1.37.0
|
||||
go.opentelemetry.io/otel/metric v1.37.0
|
||||
|
|
@ -22,6 +27,7 @@ require (
|
|||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
|
|
@ -29,6 +31,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f h1:QQB6SuvGZjK8kdc2YaLJpYhV8fxauOsjE6jgcL6YJ8Q=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ foo_seconds_total{A="B",C="D",E="true",F="42",otel_scope_fizz="buzz",otel_scope_
|
|||
foo_seconds_total{A="D",C="B",E="true",F="42",otel_scope_fizz="buzz",otel_scope_name="testmeter",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 5
|
||||
# HELP target_info Target metadata
|
||||
# TYPE target_info gauge
|
||||
target_info{"service.name"="prometheus_test","telemetry.sdk.language"="go","telemetry.sdk.name"="opentelemetry","telemetry.sdk.version"="latest"} 1
|
||||
target_info{service_name="prometheus_test",telemetry_sdk_language="go",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="latest"} 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# HELP foo_total a simple counter
|
||||
# TYPE foo_total counter
|
||||
foo_total{A="B",C="D",E="true",F="42",otel_scope_fizz="buzz",otel_scope_name="testmeter",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 24.3
|
||||
foo_total{A="D",C="B",E="true",F="42",otel_scope_fizz="buzz",otel_scope_name="testmeter",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 5
|
||||
# HELP target_info Target metadata
|
||||
# TYPE target_info gauge
|
||||
target_info{"service.name"="prometheus_test","telemetry.sdk.language"="go","telemetry.sdk.name"="opentelemetry","telemetry.sdk.version"="latest"} 1
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# HELP "foo_seconds_total" a simple counter
|
||||
# TYPE "foo_seconds_total" counter
|
||||
{"foo_seconds_total",A="B",C="D",E="true",F="42",otel_scope_fizz="buzz",otel_scope_name="testmeter",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 24.3
|
||||
{"foo_seconds_total",A="D",C="B",E="true",F="42",otel_scope_fizz="buzz",otel_scope_name="testmeter",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 5
|
||||
# HELP target_info Target metadata
|
||||
# TYPE target_info gauge
|
||||
target_info{service_name="prometheus_test",telemetry_sdk_language="go",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="latest"} 1
|
||||
|
|
@ -40,3 +40,5 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
|
|||
replace go.opentelemetry.io/otel/sdk => ../../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -29,3 +29,5 @@ require (
|
|||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -30,3 +30,5 @@ replace go.opentelemetry.io/otel => ../..
|
|||
replace go.opentelemetry.io/otel/sdk => ../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golangci/golangci-lint/v2 v2.2.2
|
||||
github.com/golangci/golangci-lint/v2 v2.3.0
|
||||
github.com/jcchavezs/porto v0.7.0
|
||||
github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad
|
||||
go.opentelemetry.io/build-tools/crosslink v0.24.0
|
||||
|
|
@ -153,7 +153,7 @@ require (
|
|||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pjbgf/sha1cd v0.4.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
|
|
@ -177,7 +177,7 @@ require (
|
|||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.5 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.7 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
|
|
@ -226,7 +226,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250721140356-96f361d9aaf7 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP
|
|||
github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
|
||||
github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6 h1:jlKy3uQkETB3zMBK8utduvojT+If2nDAM1pWpEzXjaY=
|
||||
github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6/go.mod h1:OyaRySOXorMn8zJqFku8YsKptIhPkANyKKTMC+rqMCs=
|
||||
github.com/golangci/golangci-lint/v2 v2.2.2 h1:vuzwYGPzDx9WqN85Fu8F8wqDSZqk8q76ypnznXk6MvM=
|
||||
github.com/golangci/golangci-lint/v2 v2.2.2/go.mod h1:F0BBgvaCGCAfnrlRQ729JSLrAJT172WwOpaQrbluz8E=
|
||||
github.com/golangci/golangci-lint/v2 v2.3.0 h1:SgxoaAXH8vMuuSnvRDjfF0sxWeIplxJTcs4o6gGEu9Q=
|
||||
github.com/golangci/golangci-lint/v2 v2.3.0/go.mod h1:9eHPNOsTOqLGSnDsfPRcOaC2m52stgt37uxsjtQwjg0=
|
||||
github.com/golangci/golines v0.0.0-20250217232252-b35a6149b587 h1:RXtAfHDBWAv49/t94l3j9Iqvy6eXL/nm56EejqrZuQc=
|
||||
github.com/golangci/golines v0.0.0-20250217232252-b35a6149b587/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ=
|
||||
github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c=
|
||||
|
|
@ -342,8 +342,8 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
|
|||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0 h1:OmWLkAFO2HUTYcU6mprnKud1Ey5pVdiVNYGO5HVicx8=
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0/go.mod h1:dCIuFlTPfQerXgGUju3VygfAFPdC5aE1mdacCDKDJcQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
|
|
@ -410,8 +410,8 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM
|
|||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
|
||||
github.com/securego/gosec/v2 v2.22.5 h1:ySws9uwOeE42DsG54v2moaJfh7r08Ev7SAYJuoMDfRA=
|
||||
github.com/securego/gosec/v2 v2.22.5/go.mod h1:AWfgrFsVewk5LKobsPWlygCHt8K91boVPyL6GUZG5NY=
|
||||
github.com/securego/gosec/v2 v2.22.7 h1:8/9P+oTYI4yIpAzccQKVsg1/90Po+JzGtAhqoHImDeM=
|
||||
github.com/securego/gosec/v2 v2.22.7/go.mod h1:510TFNDMrIPytokyHQAVLvPeDr41Yihn2ak8P+XQfNE=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
|
|
@ -605,8 +605,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b h1:DU+gwOBXU+6bO0sEyO7o/NeMlxZxCZEvI7v+J4a1zRQ=
|
||||
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
||||
golang.org/x/telemetry v0.0.0-20250721140356-96f361d9aaf7 h1:Z53b3vgJH20Us6ljHm4MNVLnJzJEjD3KrU+sNcT4vfs=
|
||||
golang.org/x/telemetry v0.0.0-20250721140356-96f361d9aaf7/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
|
@ -645,8 +645,8 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58
|
|||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY=
|
||||
golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ require (
|
|||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/otel v1.37.0
|
||||
go.opentelemetry.io/otel/metric v1.37.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0
|
||||
go.opentelemetry.io/otel/trace v1.37.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/sys v0.34.0
|
||||
|
|
@ -20,10 +22,11 @@ require (
|
|||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ./metric
|
||||
|
|
|
|||
|
|
@ -32,3 +32,5 @@ replace go.opentelemetry.io/otel/sdk => ../
|
|||
replace go.opentelemetry.io/otel/log => ../../log
|
||||
|
||||
replace go.opentelemetry.io/otel => ../..
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../metric
|
||||
|
|
|
|||
|
|
@ -34,3 +34,5 @@ replace go.opentelemetry.io/otel/sdk/log => ../
|
|||
replace go.opentelemetry.io/otel/log => ../../../log
|
||||
|
||||
replace go.opentelemetry.io/otel => ../../..
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../metric
|
||||
|
|
|
|||
|
|
@ -6,5 +6,8 @@ Package trace contains support for OpenTelemetry distributed tracing.
|
|||
|
||||
The following assumes a basic familiarity with OpenTelemetry concepts.
|
||||
See https://opentelemetry.io.
|
||||
|
||||
See [go.opentelemetry.io/otel/sdk/trace/internal/x] for information about
|
||||
the experimental features.
|
||||
*/
|
||||
package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# Experimental Features
|
||||
|
||||
The metric SDK contains features that have not yet stabilized in the OpenTelemetry specification.
|
||||
These features are added to the OpenTelemetry Go metric SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
|
||||
|
||||
These feature may change in backwards incompatible ways as feedback is applied.
|
||||
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
|
||||
|
||||
## Features
|
||||
|
||||
- [Self-Observability](#self-observability)
|
||||
|
||||
### Self-Observability
|
||||
|
||||
The SDK provides a self-observability feature that allows you to monitor the SDK itself.
|
||||
|
||||
To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`.
|
||||
|
||||
When enabled, the SDK will create the following metrics using the global `MeterProvider`:
|
||||
|
||||
- `otel.sdk.span.live`
|
||||
- `otel.sdk.span.started`
|
||||
|
||||
Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.
|
||||
|
||||
[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md
|
||||
|
||||
## Compatibility and Stability
|
||||
|
||||
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md).
|
||||
These features may be removed or modified in successive version releases, including patch versions.
|
||||
|
||||
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
|
||||
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
|
||||
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk/trace].
|
||||
package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SelfObservability is an experimental feature flag that determines if SDK
|
||||
// self-observability metrics are enabled.
|
||||
//
|
||||
// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable
|
||||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
|
||||
// will also enable this).
|
||||
var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) {
|
||||
if strings.ToLower(v) == "true" {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
})
|
||||
|
||||
// Feature is an experimental feature control flag. It provides a uniform way
|
||||
// to interact with these feature flags and parse their values.
|
||||
type Feature[T any] struct {
|
||||
key string
|
||||
parse func(v string) (T, bool)
|
||||
}
|
||||
|
||||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
|
||||
const envKeyRoot = "OTEL_GO_X_"
|
||||
return Feature[T]{
|
||||
key: envKeyRoot + suffix,
|
||||
parse: parse,
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the environment variable key that needs to be set to enable the
|
||||
// feature.
|
||||
func (f Feature[T]) Key() string { return f.key }
|
||||
|
||||
// Lookup returns the user configured value for the feature and true if the
|
||||
// user has enabled the feature. Otherwise, if the feature is not enabled, a
|
||||
// zero-value and false are returned.
|
||||
func (f Feature[T]) Lookup() (v T, ok bool) {
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
|
||||
//
|
||||
// > The SDK MUST interpret an empty value of an environment variable the
|
||||
// > same way as when the variable is unset.
|
||||
vRaw := os.Getenv(f.key)
|
||||
if vRaw == "" {
|
||||
return v, ok
|
||||
}
|
||||
return f.parse(vRaw)
|
||||
}
|
||||
|
||||
// Enabled returns if the feature is enabled.
|
||||
func (f Feature[T]) Enabled() bool {
|
||||
_, ok := f.Lookup()
|
||||
return ok
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package x
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
const key = "OTEL_GO_X_SELF_OBSERVABILITY"
|
||||
require.Equal(t, key, SelfObservability.Key())
|
||||
|
||||
t.Run("100", run(setenv(key, "100"), assertDisabled(SelfObservability)))
|
||||
t.Run("true", run(setenv(key, "true"), assertEnabled(SelfObservability, "true")))
|
||||
t.Run("True", run(setenv(key, "True"), assertEnabled(SelfObservability, "True")))
|
||||
t.Run("false", run(setenv(key, "false"), assertDisabled(SelfObservability)))
|
||||
t.Run("empty", run(assertDisabled(SelfObservability)))
|
||||
}
|
||||
|
||||
func run(steps ...func(*testing.T)) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, step := range steps {
|
||||
step(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setenv(k, v string) func(t *testing.T) { //nolint:unparam // This is a reusable test utility function.
|
||||
return func(t *testing.T) { t.Setenv(k, v) }
|
||||
}
|
||||
|
||||
func assertEnabled[T any](f Feature[T], want T) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
assert.True(t, f.Enabled(), "not enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.True(t, ok, "Lookup state")
|
||||
assert.Equal(t, want, v, "Lookup value")
|
||||
}
|
||||
}
|
||||
|
||||
func assertDisabled[T any](f Feature[T]) func(*testing.T) {
|
||||
var zero T
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
assert.False(t, f.Enabled(), "enabled")
|
||||
|
||||
v, ok := f.Lookup()
|
||||
assert.False(t, ok, "Lookup state")
|
||||
assert.Equal(t, zero, v, "Lookup value")
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +159,7 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
|
|||
provider: p,
|
||||
instrumentationScope: is,
|
||||
}
|
||||
t.initSelfObservability()
|
||||
p.namedTracer[is] = t
|
||||
}
|
||||
return t, ok
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import (
|
|||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
|
@ -496,6 +497,22 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
|
|||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
if s.tracer.selfObservabilityEnabled {
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.spanContext.IsSampled() {
|
||||
attrSamplingResult = s.tracer.spanLiveMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else {
|
||||
attrSamplingResult = s.tracer.spanLiveMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
}
|
||||
|
||||
s.tracer.spanLiveMetric.Add(context.Background(), -1, attrSamplingResult)
|
||||
}
|
||||
}()
|
||||
|
||||
sps := s.tracer.provider.getSpanProcessors()
|
||||
if len(sps) == 0 {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -22,9 +22,14 @@ import (
|
|||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
|
@ -2177,6 +2182,495 @@ func TestAddLinkToNonRecordingSpan(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSelfObservability(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
test func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics)
|
||||
}{
|
||||
{
|
||||
name: "SampledSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
tp := NewTracerProvider()
|
||||
_, span := tp.Tracer("").Start(context.Background(), "StartSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
|
||||
span.End()
|
||||
|
||||
want = metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 0, // No live spans at this point.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got = scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NonRecordingSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a tracer provider with NeverSample sampler to get non-recording spans.
|
||||
tp := NewTracerProvider(WithSampler(NeverSample()))
|
||||
tp.Tracer("").Start(context.Background(), "NonRecordingSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultDrop,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OnlyRecordingSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a tracer provider with NeverSample sampler to get non-recording spans.
|
||||
tp := NewTracerProvider(WithSampler(RecordingOnly()))
|
||||
tp.Tracer("").Start(context.Background(), "OnlyRecordingSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordOnly,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoteParentSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
// Create a remote parent context
|
||||
tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040")
|
||||
sid, _ := trace.SpanIDFromHex("0102040810203040")
|
||||
remoteCtx := trace.ContextWithRemoteSpanContext(context.Background(),
|
||||
trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceFlags: 0x1,
|
||||
Remote: true,
|
||||
}))
|
||||
|
||||
tp := NewTracerProvider()
|
||||
tp.Tracer("").Start(remoteCtx, "ChildSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginRemote,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LocalParentSpan",
|
||||
test: func(t *testing.T, scopeMetrics func() metricdata.ScopeMetrics) {
|
||||
tp := NewTracerProvider()
|
||||
ctx, parentSpan := tp.Tracer("").Start(context.Background(), "ParentSpan")
|
||||
_, childSpan := tp.Tracer("").Start(ctx, "ChildSpan")
|
||||
|
||||
want := metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 2, // Both parent and child spans are active.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1, // Parent span with no parent of its own.
|
||||
},
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginLocal,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1, // Child span with local parent.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
|
||||
childSpan.End()
|
||||
parentSpan.End()
|
||||
|
||||
want = metricdata.ScopeMetrics{
|
||||
Scope: instrumentation.Scope{
|
||||
Name: "go.opentelemetry.io/otel/sdk/trace",
|
||||
Version: sdk.Version(),
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
},
|
||||
Metrics: []metricdata.Metrics{
|
||||
{
|
||||
Name: otelconv.SDKSpanLive{}.Name(),
|
||||
Description: otelconv.SDKSpanLive{}.Description(),
|
||||
Unit: otelconv.SDKSpanLive{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 0, // No live spans after ending both.
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: otelconv.SDKSpanStarted{}.Name(),
|
||||
Description: otelconv.SDKSpanStarted{}.Description(),
|
||||
Unit: otelconv.SDKSpanStarted{}.Unit(),
|
||||
Data: metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
IsMonotonic: true,
|
||||
DataPoints: []metricdata.DataPoint[int64]{
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginNone,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
{
|
||||
Attributes: attribute.NewSet(
|
||||
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(
|
||||
otelconv.SpanParentOriginLocal,
|
||||
),
|
||||
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
),
|
||||
),
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got = scopeMetrics()
|
||||
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv("OTEL_GO_X_SELF_OBSERVABILITY", "True")
|
||||
prev := otel.GetMeterProvider()
|
||||
defer otel.SetMeterProvider(prev)
|
||||
r := metric.NewManualReader()
|
||||
mp := metric.NewMeterProvider(metric.WithReader(r))
|
||||
otel.SetMeterProvider(mp)
|
||||
|
||||
scopeMetrics := func() metricdata.ScopeMetrics {
|
||||
var got metricdata.ResourceMetrics
|
||||
err := r.Collect(context.Background(), &got)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, got.ScopeMetrics, 1)
|
||||
return got.ScopeMetrics[0]
|
||||
}
|
||||
tc.test(t, scopeMetrics)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RecordingOnly creates a Sampler that samples no traces, but enables recording.
|
||||
// The created sampler maintains any tracestate from the parent span context.
|
||||
func RecordingOnly() Sampler {
|
||||
return recordOnlySampler{}
|
||||
}
|
||||
|
||||
type recordOnlySampler struct{}
|
||||
|
||||
// ShouldSample implements Sampler interface. It always returns Record but not Sample.
|
||||
func (s recordOnlySampler) ShouldSample(p SamplingParameters) SamplingResult {
|
||||
psc := trace.SpanContextFromContext(p.ParentContext)
|
||||
return SamplingResult{
|
||||
Decision: RecordOnly,
|
||||
Tracestate: psc.TraceState(),
|
||||
}
|
||||
}
|
||||
|
||||
// Description returns description of the sampler.
|
||||
func (recordOnlySampler) Description() string {
|
||||
return "RecordingOnly"
|
||||
}
|
||||
|
||||
func TestRecordOnlySampler(t *testing.T) {
|
||||
te := NewTestExporter()
|
||||
tp := NewTracerProvider(WithSyncer(te), WithSampler(RecordingOnly()))
|
||||
|
||||
_, span := tp.Tracer("RecordOnly").Start(context.Background(), "test-span")
|
||||
|
||||
assert.True(t, span.IsRecording(), "span should be recording")
|
||||
assert.False(t, span.SpanContext().IsSampled(), "span should not be sampled")
|
||||
|
||||
span.End()
|
||||
|
||||
assert.Zero(t, te.Len(), "no spans should be exported")
|
||||
}
|
||||
|
||||
func BenchmarkTraceStart(b *testing.B) {
|
||||
tracer := NewTracerProvider().Tracer("")
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), trace.SpanContext{})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/trace/internal/x"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.36.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.36.0/otelconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
|
@ -17,10 +24,34 @@ type tracer struct {
|
|||
|
||||
provider *TracerProvider
|
||||
instrumentationScope instrumentation.Scope
|
||||
|
||||
selfObservabilityEnabled bool
|
||||
spanLiveMetric otelconv.SDKSpanLive
|
||||
spanStartedMetric otelconv.SDKSpanStarted
|
||||
}
|
||||
|
||||
var _ trace.Tracer = &tracer{}
|
||||
|
||||
func (tr *tracer) initSelfObservability() {
|
||||
if !x.SelfObservability.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
tr.selfObservabilityEnabled = true
|
||||
mp := otel.GetMeterProvider()
|
||||
m := mp.Meter("go.opentelemetry.io/otel/sdk/trace",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
metric.WithSchemaURL(semconv.SchemaURL))
|
||||
|
||||
var err error
|
||||
if tr.spanLiveMetric, err = otelconv.NewSDKSpanLive(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
if tr.spanStartedMetric, err = otelconv.NewSDKSpanStarted(m); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts a Span and returns it along with a context containing it.
|
||||
//
|
||||
// The Span is created with the provided name and as a child of any existing
|
||||
|
|
@ -46,6 +77,34 @@ func (tr *tracer) Start(
|
|||
}
|
||||
|
||||
s := tr.newSpan(ctx, name, &config)
|
||||
if tr.selfObservabilityEnabled {
|
||||
// Check if the span has a parent span and set the origin attribute accordingly.
|
||||
var attrParentOrigin attribute.KeyValue
|
||||
if psc := trace.SpanContextFromContext(ctx); psc.IsValid() {
|
||||
if psc.IsRemote() {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote)
|
||||
} else {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal)
|
||||
}
|
||||
} else {
|
||||
attrParentOrigin = tr.spanStartedMetric.AttrSpanParentOrigin(otelconv.SpanParentOriginNone)
|
||||
}
|
||||
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.SpanContext().IsSampled() && s.IsRecording() {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else if s.IsRecording() {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
} else {
|
||||
attrSamplingResult = tr.spanStartedMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop)
|
||||
}
|
||||
|
||||
tr.spanStartedMetric.Add(context.Background(), 1, attrParentOrigin, attrSamplingResult)
|
||||
}
|
||||
|
||||
if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() {
|
||||
sps := tr.provider.getSpanProcessors()
|
||||
for _, sp := range sps {
|
||||
|
|
@ -153,6 +212,20 @@ func (tr *tracer) newRecordingSpan(
|
|||
s.SetAttributes(sr.Attributes...)
|
||||
s.SetAttributes(config.Attributes()...)
|
||||
|
||||
if tr.selfObservabilityEnabled {
|
||||
// Determine the sampling result and create the corresponding attribute.
|
||||
var attrSamplingResult attribute.KeyValue
|
||||
if s.spanContext.IsSampled() {
|
||||
attrSamplingResult = tr.spanLiveMetric.AttrSpanSamplingResult(
|
||||
otelconv.SpanSamplingResultRecordAndSample,
|
||||
)
|
||||
} else {
|
||||
attrSamplingResult = tr.spanLiveMetric.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly)
|
||||
}
|
||||
|
||||
tr.spanLiveMetric.Add(context.Background(), 1, attrSamplingResult)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ require (
|
|||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 h1:qJW29YvkiJmXOYMu5Tf8lyrTp3dOS+K4z6IixtLaCf8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module-sets:
|
|||
- go.opentelemetry.io/otel/sdk/metric
|
||||
- go.opentelemetry.io/otel/trace
|
||||
experimental-metrics:
|
||||
version: v0.59.0
|
||||
version: v0.59.1
|
||||
modules:
|
||||
- go.opentelemetry.io/otel/exporters/prometheus
|
||||
experimental-logs:
|
||||
|
|
|
|||
Loading…
Reference in New Issue