diff --git a/.clomonitor.yml b/.clomonitor.yml
new file mode 100644
index 00000000000..4bc35d87f7d
--- /dev/null
+++ b/.clomonitor.yml
@@ -0,0 +1,4 @@
+# see https://github.com/cncf/clomonitor/blob/main/docs/checks.md#exemptions
+exemptions:
+ - check: artifacthub_badge
+ reason: "Artifact Hub doesn't support .NET packages"
diff --git a/.editorconfig b/.editorconfig
index cdfa4e32468..e3f693c1d13 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,6 +10,9 @@ indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
+[*.sh]
+end_of_line = lf
+
[*.{cs,cshtml,htm,html,md,py,sln,xml}]
indent_size = 4
@@ -156,6 +159,13 @@ dotnet_diagnostic.IDE0005.severity = warning
# RS0041: Public members should not use oblivious types
dotnet_diagnostic.RS0041.severity = suggestion
+[*Tests.cs]
+# CA1515: Disable making types internal for Tests classes. It is required by xunit
+dotnet_diagnostic.CA1515.severity = none
+
+# CA2007: Disable Consider calling ConfigureAwait on the awaited task. It is not working with xunit
+dotnet_diagnostic.CA2007.severity = none
+
[**/obj/**.cs]
generated_code = true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000000..50ca329f24b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.sh eol=lf
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index d503e982d1f..1d9f2dd277a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -62,7 +62,7 @@ body:
- type: textarea
attributes:
label: Steps to Reproduce
- description: Provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). We will close the issue if the repro project you share with us is complex or we cannot reproduce the behavior you are reporting. We cannot investigate custom projects, so don't point us to such, please.
+ description: Provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). We will close the issue if the repro project you share with us is complex or we cannot reproduce the behavior you are reporting. We cannot investigate custom projects, so don't point us to such, please.
validations:
required: true
@@ -84,3 +84,11 @@ body:
attributes:
label: Additional Context
description: Any additional information you think may be relevant to this issue.
+
+ - type: dropdown
+ attributes:
+ label: Tip
+ description: This element is static, used to render a helpful sub-heading for end-users and community members to help prioritize issues. Please leave as is.
+ options:
+ - "[React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with :+1: to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/)."
+ default: 0
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index ab958731736..9ca53fe8e7e 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -53,3 +53,11 @@ body:
attributes:
label: Additional context
description: Any additional information you think may be relevant to this feature request.
+
+ - type: dropdown
+ attributes:
+ label: Tip
+ description: This element is static, used to render a helpful sub-heading for end-users and community members to help prioritize issues. Please leave as is.
+ options:
+ - "[React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with :+1: to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/)."
+ default: 0
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 168e352f889..dda94ddd1f0 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,20 +1,10 @@
+# This file is retained solely for automated tooling to see we do automated
+# dependency updates as not all such scanners recognize the use of Renovate.
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
- interval: "daily"
+ interval: yearly
labels:
- "infra"
- - package-ecosystem: "dotnet-sdk"
- directory: "/"
- schedule:
- interval: "weekly"
- day: "wednesday"
- labels:
- - "infra"
- ignore:
- - dependency-name: "*"
- update-types:
- - "version-update:semver-major"
- - "version-update:semver-minor"
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 00000000000..996d775ec7f
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,70 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "additionalBranchPrefix": "{{manager}}/",
+ "automerge": false,
+ "commitBodyTable": true,
+ "commitMessageAction": "Bump",
+ "dependencyDashboard": false,
+ "extends": [
+ "config:best-practices",
+ "customManagers:dockerfileVersions",
+ "customManagers:githubActionsVersions",
+ ":automergeRequireAllStatusChecks",
+ ":disableRateLimiting",
+ ":enableVulnerabilityAlerts",
+ ":gitSignOff",
+ ":ignoreUnstable"
+ ],
+ "ignorePresets": [
+ ":ignoreModulesAndTests"
+ ],
+ "ignorePaths": [
+ "**/node_modules/**",
+ "**/bower_components/**",
+ "**/vendor/**",
+ "**/__tests__/**",
+ "**/__fixtures__/**"
+ ],
+ "labels": ["dependencies", "infra"],
+ "packageRules": [
+ {
+ "matchManagers": ["dockerfile"],
+ "addLabels": ["docker"]
+ },
+ {
+ "matchManagers": ["github-actions"],
+ "addLabels": ["github_actions"]
+ },
+ {
+ "matchManagers": ["nuget"],
+ "addLabels": [".NET"]
+ },
+ {
+ "matchManagers": ["pypi"],
+ "addLabels": ["python"]
+ },
+ {
+ "description": ["Skip pinned NuGet package versions"],
+ "matchManagers": ["nuget"],
+ "matchCurrentValue": "^\\[[^,]+,\\)$",
+ "enabled": false
+ },
+ {
+ "extends": ["monorepo:dotnet"],
+ "description": ["Disable major version updates for .NET"],
+ "matchUpdateTypes": ["major"],
+ "enabled": false
+ },
+ {
+ "matchDepNames": ["xunit"],
+ "description": ["Disable major version updates for xunit"],
+ "matchUpdateTypes": ["major"],
+ "enabled": false
+ }
+ ],
+ "schedule": ["* 8-17 * * 3"],
+ "timezone": "Etc/UTC",
+ "vulnerabilityAlerts": {
+ "addLabels": ["security"]
+ }
+}
diff --git a/.github/workflows/Component.BuildTest.yml b/.github/workflows/Component.BuildTest.yml
index 4cffbbe989a..406be841884 100644
--- a/.github/workflows/Component.BuildTest.yml
+++ b/.github/workflows/Component.BuildTest.yml
@@ -20,7 +20,7 @@ on:
required: false
type: string
os-list:
- default: '[ "windows-latest", "ubuntu-22.04", "otel-linux-arm64" ]'
+ default: '[ "windows-latest", "windows-11-arm", "ubuntu-22.04", "ubuntu-22.04-arm" ]'
required: false
type: string
tfm-list:
@@ -28,6 +28,9 @@ on:
required: false
type: string
+permissions:
+ contents: read
+
jobs:
build-test:
@@ -39,22 +42,31 @@ jobs:
exclude:
- os: ubuntu-22.04
version: net462
- - os: otel-linux-arm64
+ - os: ubuntu-22.04-arm
version: net462
- - os: otel-linux-arm64
+ - os: ubuntu-22.04-arm
+ version: net8.0
+ - os: windows-11-arm
version: net8.0
runs-on: ${{ matrix.os }}
+ timeout-minutes: 30
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit. MinVer needs to find
# the version tag which is typically NOT on the first commit so we
# retrieve them all.
fetch-depth: 0
- - name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ - name: Setup previous .NET runtimes
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
+ with:
+ dotnet-version: |
+ 8.0.x
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: dotnet restore ${{ inputs.project-name }}
run: dotnet restore ${{ inputs.project-name }} ${{ inputs.project-build-commands }}
@@ -63,7 +75,18 @@ jobs:
run: dotnet build ${{ inputs.project-name }} --configuration Release --no-restore ${{ inputs.project-build-commands }}
- name: dotnet test ${{ inputs.project-name }}
- run: dotnet test ${{ inputs.project-name }} --collect:"Code Coverage" --results-directory:TestResults --framework ${{ matrix.version }} --configuration Release --no-restore --no-build --logger:"console;verbosity=detailed" -- RunConfiguration.DisableAppDomain=true
+ run: >
+ dotnet test ${{ inputs.project-name }}
+ --collect:"Code Coverage"
+ --results-directory:TestResults
+ --framework ${{ matrix.version }}
+ --configuration Release
+ --no-restore
+ --no-build
+ --logger:"console;verbosity=detailed"
+ --logger:"GitHubActions;report-warnings=false"
+ --logger:"junit;LogFilePath=TestResults/junit.xml"
+ -- RunConfiguration.DisableAppDomain=true
- name: Install coverage tool
run: dotnet tool install -g dotnet-coverage
@@ -72,15 +95,24 @@ jobs:
run: dotnet-coverage merge -f cobertura -o ./TestResults/Cobertura.xml ./TestResults/**/*.coverage
- name: Upload code coverage ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }}
- uses: codecov/codecov-action@v5
+ uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
continue-on-error: true # Note: Don't fail for upload failures
env:
OS: ${{ matrix.os }}
TFM: ${{ matrix.version }}
- token: ${{ secrets.CODECOV_TOKEN }}
with:
- file: TestResults/Cobertura.xml
+ files: TestResults/Cobertura.xml
env_vars: OS,TFM
flags: ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }}
name: Code Coverage for ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }} on [${{ matrix.os }}.${{ matrix.version }}]
codecov_yml_path: .github/codecov.yml
+ token: ${{ secrets.CODECOV_TOKEN }}
+
+ - name: Upload test results ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }}
+ if: ${{ !cancelled() && hashFiles('./**/TestResults/junit.xml') != '' }}
+ uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
+ with:
+ env_vars: OS,TFM
+ flags: ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }}
+ name: Test results for ${{ inputs.code-cov-prefix }}-${{ inputs.code-cov-name }} on [${{ matrix.os }}.${{ matrix.version }}]
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/add-labels.yml b/.github/workflows/add-labels.yml
index 48639374fb2..19d25be7e44 100644
--- a/.github/workflows/add-labels.yml
+++ b/.github/workflows/add-labels.yml
@@ -7,18 +7,19 @@ on:
branches: [ 'main*' ]
permissions:
- issues: write
- pull-requests: write
+ contents: read
jobs:
add-labels-on-issues:
+ permissions:
+ issues: write
if: github.event_name == 'issues' && !github.event.issue.pull_request
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Add labels for package found in bug issue descriptions
shell: pwsh
@@ -33,13 +34,15 @@ jobs:
ISSUE_BODY: ${{ github.event.issue.body }}
add-labels-on-pull-requests:
+ permissions:
+ pull-requests: write
if: github.event_name == 'pull_request_target'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.repository.default_branch }} # Note: Do not run on the PR branch we want to execute add-labels.psm1 from main on the base repo only because pull_request_target can see secrets
diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml
index 9ea74f72211..2daa116be62 100644
--- a/.github/workflows/automation.yml
+++ b/.github/workflows/automation.yml
@@ -5,30 +5,33 @@ on:
outputs:
enabled:
value: ${{ jobs.resolve-automation.outputs.enabled == 'true' }}
- token-secret-name:
- value: ${{ jobs.resolve-automation.outputs.token-secret-name }}
username:
value: ${{ vars.AUTOMATION_USERNAME }}
email:
value: ${{ vars.AUTOMATION_EMAIL }}
+ application-name:
+ value: ${{ vars.AUTOMATION_APPLICATION_NAME }}
+ application-username:
+ value: ${{ vars.AUTOMATION_APPLICATION_USERNAME }}
secrets:
- OPENTELEMETRYBOT_GITHUB_TOKEN:
+ OTELBOT_DOTNET_PRIVATE_KEY:
required: false
+permissions:
+ contents: read
+
jobs:
resolve-automation:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
outputs:
enabled: ${{ steps.evaluate.outputs.enabled }}
- token-secret-name: ${{ steps.evaluate.outputs.token-secret-name }}
env:
- OPENTELEMETRYBOT_GITHUB_TOKEN_EXISTS: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN != '' }}
+ OTELBOT_DOTNET_PRIVATE_KEY_EXISTS: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY != '' }}
steps:
- id: evaluate
run: |
- echo "enabled=${{ env.OPENTELEMETRYBOT_GITHUB_TOKEN_EXISTS == 'true' }}" >> "$GITHUB_OUTPUT"
- echo "token-secret-name=OPENTELEMETRYBOT_GITHUB_TOKEN" >> "$GITHUB_OUTPUT"
+ echo "enabled=${{ env.OTELBOT_DOTNET_PRIVATE_KEY_EXISTS == 'true' }}" >> "$GITHUB_OUTPUT"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5640ee079b5..3110eef0cc6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,17 +9,27 @@ on:
pull_request:
branches: [ 'main*' ]
+permissions:
+ contents: read
+
jobs:
lint-misspell-sanitycheck:
uses: ./.github/workflows/sanitycheck.yml
+ code-ql:
+ uses: ./.github/workflows/codeql-analysis-steps.yml
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
detect-changes:
runs-on: windows-latest
outputs:
changes: ${{ steps.changes.outputs.changes }}
steps:
- - uses: actions/checkout@v4
- - uses: AurorNZ/paths-filter@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - uses: AurorNZ/paths-filter@3b1f3abc3371cca888d8eb03dfa70bc8a9867629 # v4.0.0
id: changes
with:
filters: |
@@ -107,13 +117,13 @@ jobs:
|| contains(needs.detect-changes.outputs.changes, 'otlp')
|| contains(needs.detect-changes.outputs.changes, 'build')
|| contains(needs.detect-changes.outputs.changes, 'shared')
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
version: [ net8.0, net9.0 ]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Run OTLP Exporter docker compose
run: docker compose --file=test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/docker-compose.yml --file=build/docker-compose.${{ matrix.version }}.yml --project-directory=. up --exit-code-from=tests --build
@@ -125,13 +135,13 @@ jobs:
|| contains(needs.detect-changes.outputs.changes, 'instrumentation')
|| contains(needs.detect-changes.outputs.changes, 'build')
|| contains(needs.detect-changes.outputs.changes, 'shared')
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
version: [ net8.0, net9.0 ]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Run W3C Trace Context docker compose
run: docker compose --file=test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/docker-compose.yml --file=build/docker-compose.${{ matrix.version }}.yml --project-directory=. up --exit-code-from=tests --build
@@ -172,6 +182,7 @@ jobs:
build-test:
needs: [
detect-changes,
+ code-ql,
lint-misspell-sanitycheck,
lint-md,
lint-dotnet-format,
@@ -187,7 +198,17 @@ jobs:
concurrency-tests
]
if: always() && !cancelled()
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - run: |
- if ( ${{ contains(needs.*.result, 'failure') }} == true ); then echo 'build failed'; exit 1; else echo 'build complete'; fi
+ - name: Report CI status
+ shell: bash
+ env:
+ CI_SUCCESS: ${{ !contains(needs.*.result, 'failure') }}
+ run: |
+ if [ "${CI_SUCCESS}" == "true" ]
+ then
+ echo 'Build complete'
+ else
+ echo 'Build failed'
+ exit 1
+ fi
diff --git a/.github/workflows/codeql-analysis-steps.yml b/.github/workflows/codeql-analysis-steps.yml
new file mode 100644
index 00000000000..47a6ba61535
--- /dev/null
+++ b/.github/workflows/codeql-analysis-steps.yml
@@ -0,0 +1,65 @@
+name: codeql-analysis-steps
+
+on:
+ workflow_call:
+
+permissions: {}
+
+jobs:
+ analyze:
+ permissions:
+ actions: read # for github/codeql-action/init to get workflow details
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/analyze to upload SARIF results
+ runs-on: windows-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['actions', 'csharp']
+
+ steps:
+ - name: Configure Pagefile
+ if: matrix.language == 'csharp'
+ uses: al-cheb/configure-pagefile-action@a3b6ebd6b634da88790d9c58d4b37a7f4a7b8708 # v1.4
+ with:
+ minimum-size: 8GB
+ maximum-size: 32GB
+ disk-root: "D:"
+
+ - name: Checkout repository
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ with:
+ filter: 'tree:0'
+ persist-credentials: false
+ show-progress: false
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@303c0aef88fc2fe5ff6d63d3b1596bfd83dfa1f9 # v3.30.4
+ with:
+ build-mode: none
+ languages: ${{ matrix.language }}
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@303c0aef88fc2fe5ff6d63d3b1596bfd83dfa1f9 # v3.30.4
+ with:
+ category: '/language:${{ matrix.language }}'
+
+ results:
+ if: ${{ !cancelled() }}
+ needs: [ analyze ]
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Report status
+ shell: bash
+ env:
+ SCAN_SUCCESS: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
+ run: |
+ if [ "${SCAN_SUCCESS}" == "true" ]
+ then
+ echo 'CodeQL analysis successful'
+ else
+ echo 'CodeQL analysis failed'
+ exit 1
+ fi
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 3cdd6a57b6d..44fc56c2444 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,8 +1,3 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
name: "CodeQL"
on:
@@ -10,37 +5,12 @@ on:
- cron: '0 0 * * *' # once in a day at 00:00
workflow_dispatch:
-jobs:
- analyze:
- name: Analyze
- runs-on: windows-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: ['csharp']
-
- steps:
- - name: configure Pagefile
- uses: al-cheb/configure-pagefile-action@v1.4
- with:
- minimum-size: 8GB
- maximum-size: 32GB
- disk-root: "D:"
-
- - name: Checkout repository
- uses: actions/checkout@v4
+permissions: {}
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: ${{ matrix.language }}
-
- - name: Setup dotnet
- uses: actions/setup-dotnet@v4
-
- - name: dotnet pack
- run: dotnet pack ./build/OpenTelemetry.proj --configuration Release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+jobs:
+ code-ql:
+ uses: ./.github/workflows/codeql-analysis-steps.yml
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
diff --git a/.github/workflows/concurrency-tests.yml b/.github/workflows/concurrency-tests.yml
index 907d7260049..ae1e64bfcf9 100644
--- a/.github/workflows/concurrency-tests.yml
+++ b/.github/workflows/concurrency-tests.yml
@@ -5,6 +5,9 @@ name: Concurrency Tests
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-concurrency-tests:
@@ -17,10 +20,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: Run Coyote Tests
shell: pwsh
@@ -28,7 +31,7 @@ jobs:
- name: Publish Artifacts
if: always() && !cancelled()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ matrix.os }}-${{ matrix.project }}-${{ matrix.version }}-coyoteoutput
path: '**/*_CoyoteOutput.*'
diff --git a/.github/workflows/docfx.yml b/.github/workflows/docfx.yml
index 76ab4f2e1ec..fcf07c097b3 100644
--- a/.github/workflows/docfx.yml
+++ b/.github/workflows/docfx.yml
@@ -5,16 +5,19 @@ name: Build docfx
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-docfx-build:
runs-on: windows-latest
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: install docfx
run: dotnet tool install -g docfx
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index 2ea4834570f..dc72da32565 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -5,16 +5,19 @@ name: Lint - dotnet format
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-dotnet-format-stable:
runs-on: windows-latest
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: dotnet restore
run: dotnet restore OpenTelemetry.sln
@@ -29,10 +32,10 @@ jobs:
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: dotnet restore
run: dotnet restore OpenTelemetry.sln
diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml
new file mode 100644
index 00000000000..f86b5a99322
--- /dev/null
+++ b/.github/workflows/fossa.yml
@@ -0,0 +1,20 @@
+name: FOSSA scanning
+
+on:
+ push:
+ branches:
+ - main
+
+permissions:
+ contents: read
+
+jobs:
+ fossa:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+
+ - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0
+ with:
+ api-key: ${{secrets.FOSSA_API_KEY}}
+ team: OpenTelemetry
diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml
index b013c52a57a..2ce6431d9cc 100644
--- a/.github/workflows/markdownlint.yml
+++ b/.github/workflows/markdownlint.yml
@@ -5,16 +5,19 @@ name: Lint - Markdown
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-markdownlint:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: run markdownlint
- uses: DavidAnson/markdownlint-cli2-action@v19.1.0
+ uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e # v20.0.0
with:
globs: |
**/*.md
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
new file mode 100644
index 00000000000..3d950859b51
--- /dev/null
+++ b/.github/workflows/ossf-scorecard.yml
@@ -0,0 +1,47 @@
+name: OSSF Scorecard
+
+on:
+ push:
+ branches:
+ - main
+ schedule:
+ - cron: "24 5 * * 0" # once a week
+ workflow_dispatch:
+
+permissions: read-all
+
+jobs:
+ analysis:
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed for Code scanning upload
+ security-events: write
+ # Needed for GitHub OIDC token if publish_results is true
+ id-token: write
+ steps:
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ with:
+ persist-credentials: false
+
+ - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable
+ # uploads of run results in SARIF format to the repository Actions tab.
+ # https://docs.github.com/en/actions/advanced-guides/storing-workflow-data-as-artifacts
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # 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@303c0aef88fc2fe5ff6d63d3b1596bfd83dfa1f9 # v3.30.4
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/package-validation.yml b/.github/workflows/package-validation.yml
index d1f7658ee2d..3216646f45c 100644
--- a/.github/workflows/package-validation.yml
+++ b/.github/workflows/package-validation.yml
@@ -5,12 +5,15 @@ name: Package Validation
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-package-validation-stable:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit. MinVer needs to find
# the version tag which is typically NOT on the first commit so we
@@ -18,16 +21,23 @@ jobs:
fetch-depth: 0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: dotnet pack
run: dotnet pack ./build/OpenTelemetry.proj --configuration Release /p:EnablePackageValidation=true /p:ExposeExperimentalFeatures=false /p:RunningDotNetPack=true
+ - name: Publish stable NuGet packages to Artifacts
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: packages-stable
+ path: ./artifacts/package/release
+ if-no-files-found: error
+
run-package-validation-experimental:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit. MinVer needs to find
# the version tag which is typically NOT on the first commit so we
@@ -35,7 +45,14 @@ jobs:
fetch-depth: 0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: dotnet pack
run: dotnet pack ./build/OpenTelemetry.proj --configuration Release /p:EnablePackageValidation=true /p:ExposeExperimentalFeatures=true /p:RunningDotNetPack=true
+
+ - name: Publish experimental NuGet packages to Artifacts
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: packages-experimental
+ path: ./artifacts/package/release
+ if-no-files-found: error
diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml
index bb67ead1e47..7a06a0c6580 100644
--- a/.github/workflows/post-release.yml
+++ b/.github/workflows/post-release.yml
@@ -16,107 +16,148 @@ on:
types:
- created
+permissions:
+ contents: read
+
jobs:
automation:
uses: ./.github/workflows/automation.yml
- secrets: inherit
+ secrets:
+ OTELBOT_DOTNET_PRIVATE_KEY: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
push-packages-and-publish-release:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
+ permissions:
+ id-token: write
+
if: |
- github.event_name == 'issue_comment'
- && github.event.issue.pull_request
- && github.event.issue.locked == true
- && github.event.comment.user.login != needs.automation.outputs.username
- && contains(github.event.comment.body, '/PushPackages')
- && startsWith(github.event.issue.title, '[release] Prepare release ')
- && github.event.issue.pull_request.merged_at
- && needs.automation.outputs.enabled
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ github.event_name == 'issue_comment' &&
+ github.event.issue.pull_request &&
+ github.event.issue.locked == true &&
+ github.event.comment.user.login != needs.automation.outputs.application-username &&
+ contains(github.event.comment.body, '/PushPackages') &&
+ startsWith(github.event.issue.title, '[release] Prepare release ') &&
+ github.event.issue.pull_request.merged_at &&
+ needs.automation.outputs.enabled
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
ref: ${{ github.event.repository.default_branch }}
+ - name: Setup .NET
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
+
+ - name: NuGet log in
+ uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544 # v1.1.0
+ id: nuget-login
+ with:
+ user: ${{ secrets.NUGET_USER }}
+
- name: Push packages and publish release
shell: pwsh
env:
- NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ COMMENT_USER_NAME: ${{ github.event.comment.user.login }}
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
+ NUGET_TOKEN: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
run: |
Import-Module .\build\scripts\post-release.psm1
+ $HasToken = -Not [string]::IsNullOrEmpty($env:NUGET_TOKEN)
PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullRequest `
- -gitRepository '${{ github.repository }}' `
- -pullRequestNumber '${{ github.event.issue.number }}' `
- -botUserName '${{ needs.automation.outputs.username }}' `
- -commentUserName '${{ github.event.comment.user.login }}' `
- -artifactDownloadPath '${{ github.workspace }}/artifacts' `
- -pushToNuget '${{ secrets.NUGET_TOKEN != '' }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -pullRequestNumber ${env:ISSUE_NUMBER} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME} `
+ -commentUserName ${env:COMMENT_USER_NAME} `
+ -artifactDownloadPath "${env:GITHUB_WORKSPACE}/artifacts" `
+ -pushToNuget $HasToken
post-release-published:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs:
- automation
if: |
- needs.automation.outputs.enabled
- && (github.event_name == 'release' || github.event_name == 'workflow_dispatch')
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ needs.automation.outputs.enabled &&
+ (github.event_name == 'release' || github.event_name == 'workflow_dispatch')
steps:
- - uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit. We need all the tags
# for this work.
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: Create GitHub Pull Request to update stable build version in props
if: |
(github.ref_type == 'tag' && startsWith(github.ref_name, 'core-') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && !contains(github.ref_name, '-rc'))
|| (inputs.tag && startsWith(inputs.tag, 'core-') && !contains(inputs.tag, '-alpha') && !contains(inputs.tag, '-beta') && !contains(inputs.tag, '-rc'))
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ BOT_USER_EMAIL: ${{ needs.automation.outputs.email }}
+ BOT_USER_NAME: ${{ needs.automation.outputs.username }}
+ TAG: ${{ inputs.tag || github.ref_name }}
+ TARGET_BRANCH: ${{ github.event.repository.default_branch }}
run: |
Import-Module .\build\scripts\post-release.psm1
CreateStableVersionUpdatePullRequest `
- -gitRepository '${{ github.repository }}' `
- -tag '${{ inputs.tag || github.ref_name }}' `
- -targetBranch '${{ github.event.repository.default_branch }}' `
- -gitUserName '${{ needs.automation.outputs.username }}' `
- -gitUserEmail '${{ needs.automation.outputs.email }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -tag ${env:TAG} `
+ -targetBranch ${env:TARGET_BRANCH} `
+ -gitUserName ${env:BOT_USER_NAME} `
+ -gitUserEmail ${env:BOT_USER_EMAIL}
- name: Invoke core version update workflow in opentelemetry-dotnet-contrib repository
if: vars.CONTRIB_REPO
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ CONTRIB_REPO: ${{ vars.CONTRIB_REPO }}
+ TAG: ${{ inputs.tag || github.ref_name }}
run: |
Import-Module .\build\scripts\post-release.psm1
InvokeCoreVersionUpdateWorkflowInRemoteRepository `
- -remoteGitRepository '${{ vars.CONTRIB_REPO }}' `
- -tag '${{ inputs.tag || github.ref_name }}'
+ -remoteGitRepository ${env:CONTRIB_REPO} `
+ -tag ${env:TAG}
- name: Post notice when release is published
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ TAG: ${{ inputs.tag || github.ref_name }}
run: |
Import-Module .\build\scripts\post-release.psm1
TryPostReleasePublishedNoticeOnPrepareReleasePullRequest `
- -gitRepository '${{ github.repository }}' `
- -botUserName '${{ needs.automation.outputs.username }}' `
- -tag '${{ inputs.tag || github.ref_name }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME} `
+ -tag ${env:TAG}
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
index fbccc656063..5d8eb56ae39 100644
--- a/.github/workflows/prepare-release.yml
+++ b/.github/workflows/prepare-release.yml
@@ -23,187 +23,238 @@ on:
types:
- created
+permissions:
+ contents: read
+
jobs:
automation:
uses: ./.github/workflows/automation.yml
- secrets: inherit
+ secrets:
+ OTELBOT_DOTNET_PRIVATE_KEY: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
prepare-release-pr:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
if: github.event_name == 'workflow_dispatch' && needs.automation.outputs.enabled
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
-
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Create GitHub Pull Request to prepare release
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ BOT_USER_EMAIL: ${{ needs.automation.outputs.email }}
+ BOT_USER_NAME: ${{ needs.automation.outputs.username }}
+ COMMENT_USER_NAME: ${{ github.event.sender.login }}
+ TAG_PREFIX: ${{ inputs.tag-prefix }}
+ VERSION: ${{ inputs.version }}
run: |
Import-Module .\build\scripts\prepare-release.psm1
CreatePullRequestToUpdateChangelogsAndPublicApis `
- -gitRepository '${{ github.repository }}' `
- -minVerTagPrefix '${{ inputs.tag-prefix }}' `
- -version '${{ inputs.version }}' `
- -requestedByUserName '${{ github.event.sender.login }}' `
- -targetBranch '${{ github.ref_name }}' `
- -gitUserName '${{ needs.automation.outputs.username }}' `
- -gitUserEmail '${{ needs.automation.outputs.email }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -minVerTagPrefix ${env:TAG_PREFIX} `
+ -version ${env:VERSION} `
+ -requestedByUserName ${env:COMMENT_USER_NAME} `
+ -targetBranch ${env:GITHUB_REF_NAME} `
+ -gitUserName ${env:BOT_USER_NAME} `
+ -gitUserEmail ${env:BOT_USER_EMAIL}
lock-pr-and-post-notice-to-create-release-tag:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
if: |
- github.event_name == 'pull_request'
- && github.event.action == 'closed'
- && github.event.pull_request.user.login == needs.automation.outputs.username
- && github.event.pull_request.merged == true
- && startsWith(github.event.pull_request.title, '[release] Prepare release ')
- && needs.automation.outputs.enabled
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ github.event_name == 'pull_request' &&
+ github.event.action == 'closed' &&
+ github.event.pull_request.user.login == needs.automation.outputs.application-username &&
+ github.event.pull_request.merged == true &&
+ startsWith(github.event.pull_request.title, '[release] Prepare release ') &&
+ needs.automation.outputs.enabled
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Lock GitHub Pull Request to prepare release
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ ISSUE_NUMBER: ${{ github.event.pull_request.number }}
run: |
Import-Module .\build\scripts\prepare-release.psm1
LockPullRequestAndPostNoticeToCreateReleaseTag `
- -gitRepository '${{ github.repository }}' `
- -pullRequestNumber '${{ github.event.pull_request.number }}' `
- -botUserName '${{ needs.automation.outputs.username }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -pullRequestNumber ${env:ISSUE_NUMBER} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME}
create-release-tag-pr-post-notice:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
if: |
- github.event_name == 'issue_comment'
- && github.event.issue.pull_request
- && github.event.issue.locked == true
- && github.event.comment.user.login != needs.automation.outputs.username
- && contains(github.event.comment.body, '/CreateReleaseTag')
- && startsWith(github.event.issue.title, '[release] Prepare release ')
- && github.event.issue.pull_request.merged_at
- && needs.automation.outputs.enabled
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ github.event_name == 'issue_comment' &&
+ github.event.issue.pull_request &&
+ github.event.issue.locked == true &&
+ github.event.comment.user.login != needs.automation.outputs.application-username &&
+ contains(github.event.comment.body, '/CreateReleaseTag') &&
+ startsWith(github.event.issue.title, '[release] Prepare release ') &&
+ github.event.issue.pull_request.merged_at &&
+ needs.automation.outputs.enabled
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit which fails the git tag operation below
fetch-depth: 0
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Create release tag
id: create-tag
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ BOT_USER_EMAIL: ${{ needs.automation.outputs.email }}
+ BOT_USER_NAME: ${{ needs.automation.outputs.username }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
Import-Module .\build\scripts\prepare-release.psm1
CreateReleaseTagAndPostNoticeOnPullRequest `
- -gitRepository '${{ github.repository }}' `
- -pullRequestNumber '${{ github.event.issue.number }}' `
- -botUserName '${{ needs.automation.outputs.username }}' `
- -gitUserName '${{ needs.automation.outputs.username }}' `
- -gitUserEmail '${{ needs.automation.outputs.email }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -pullRequestNumber ${env:ISSUE_NUMBER} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME} `
+ -gitUserName ${env:BOT_USER_NAME} `
+ -gitUserEmail ${env:BOT_USER_EMAIL}
update-changelog-release-dates-on-prepare-pr-post-notice:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
if: |
- github.event_name == 'issue_comment'
- && github.event.issue.pull_request
- && github.event.issue.state == 'open'
- && github.event.comment.user.login != needs.automation.outputs.username
- && contains(github.event.comment.body, '/UpdateReleaseDates')
- && startsWith(github.event.issue.title, '[release] Prepare release ')
- && github.event.issue.pull_request.merged_at == null
- && needs.automation.outputs.enabled
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ github.event_name == 'issue_comment' &&
+ github.event.issue.pull_request &&
+ github.event.issue.state == 'open' &&
+ github.event.comment.user.login != needs.automation.outputs.application-username &&
+ contains(github.event.comment.body, '/UpdateReleaseDates') &&
+ startsWith(github.event.issue.title, '[release] Prepare release ') &&
+ github.event.issue.pull_request.merged_at == null &&
+ needs.automation.outputs.enabled
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit which fails the git tag operation below
fetch-depth: 0
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Update release date
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ BOT_USER_EMAIL: ${{ needs.automation.outputs.email }}
+ BOT_USER_NAME: ${{ needs.automation.outputs.username }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ COMMENT_USER_NAME: ${{ github.event.comment.user.login }}
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
Import-Module .\build\scripts\prepare-release.psm1
UpdateChangelogReleaseDatesAndPostNoticeOnPullRequest `
- -gitRepository '${{ github.repository }}' `
- -pullRequestNumber '${{ github.event.issue.number }}' `
- -botUserName '${{ needs.automation.outputs.username }}' `
- -commentUserName '${{ github.event.comment.user.login }}' `
- -gitUserName '${{ needs.automation.outputs.username }}' `
- -gitUserEmail '${{ needs.automation.outputs.email }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -pullRequestNumber ${env:ISSUE_NUMBER} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME} `
+ -commentUserName ${env:COMMENT_USER_NAME} `
+ -gitUserName ${env:BOT_USER_NAME} `
+ -gitUserEmail ${env:BOT_USER_EMAIL}
update-releasenotes-on-prepare-pr-post-notice:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs: automation
if: |
- github.event_name == 'issue_comment'
- && github.event.issue.pull_request
- && github.event.issue.state == 'open'
- && github.event.comment.user.login != needs.automation.outputs.username
- && contains(github.event.comment.body, '/UpdateReleaseNotes')
- && startsWith(github.event.issue.title, '[release] Prepare release ')
- && github.event.issue.pull_request.merged_at == null
- && needs.automation.outputs.enabled
-
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ github.event_name == 'issue_comment' &&
+ github.event.issue.pull_request &&
+ github.event.issue.state == 'open' &&
+ github.event.comment.user.login != needs.automation.outputs.application-username &&
+ contains(github.event.comment.body, '/UpdateReleaseNotes') &&
+ startsWith(github.event.issue.title, '[release] Prepare release ') &&
+ github.event.issue.pull_request.merged_at == null &&
+ needs.automation.outputs.enabled
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
+ with:
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit which fails the git tag operation below
fetch-depth: 0
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Update release notes
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ BOT_USER_EMAIL: ${{ needs.automation.outputs.email }}
+ BOT_USER_NAME: ${{ needs.automation.outputs.username }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ COMMENT_BODY: ${{ github.event.comment.body }}
+ COMMENT_USER_NAME: ${{ github.event.comment.user.login }}
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
shell: pwsh
run: |
Import-Module .\build\scripts\prepare-release.psm1
UpdateReleaseNotesAndPostNoticeOnPullRequest `
- -gitRepository '${{ github.repository }}' `
- -pullRequestNumber '${{ github.event.issue.number }}' `
- -botUserName '${{ needs.automation.outputs.username }}' `
- -commentUserName '${{ github.event.comment.user.login }}' `
- -commentBody '${{ github.event.comment.body }}' `
- -gitUserName '${{ needs.automation.outputs.username }}' `
- -gitUserEmail '${{ needs.automation.outputs.email }}'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -pullRequestNumber ${env:ISSUE_NUMBER} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME} `
+ -commentUserName ${env:COMMENT_USER_NAME} `
+ -commentBody $Env:COMMENT_BODY `
+ -gitUserName ${env:BOT_USER_NAME} `
+ -gitUserEmail ${env:BOT_USER_EMAIL}
diff --git a/.github/workflows/publish-packages-1.0.yml b/.github/workflows/publish-packages-1.0.yml
index 1d4d699381b..86661100080 100644
--- a/.github/workflows/publish-packages-1.0.yml
+++ b/.github/workflows/publish-packages-1.0.yml
@@ -16,10 +16,14 @@ on:
schedule:
- cron: '0 0 * * *' # once in a day at 00:00
+permissions:
+ contents: read
+
jobs:
automation:
uses: ./.github/workflows/automation.yml
- secrets: inherit
+ secrets:
+ OTELBOT_DOTNET_PRIVATE_KEY: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
build-pack-publish:
runs-on: windows-latest
@@ -35,7 +39,7 @@ jobs:
artifact-id: ${{ steps.upload-artifacts.outputs.artifact-id }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Note: By default GitHub only fetches 1 commit. MinVer needs to find
# the version tag which is typically NOT on the first commit so we
@@ -43,18 +47,19 @@ jobs:
fetch-depth: 0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: Install Cosign
- uses: sigstore/cosign-installer@v3
+ uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
with:
- cosign-release: v2.4.0
+ cosign-release: v2.5.3
- name: dotnet restore
run: dotnet restore ./build/OpenTelemetry.proj -p:RunningDotNetPack=true
- name: dotnet build
- run: dotnet build ./build/OpenTelemetry.proj --configuration Release --no-restore -p:Deterministic=true -p:BuildNumber=${{ github.run_number }} -p:RunningDotNetPack=true
+ shell: pwsh
+ run: dotnet build ./build/OpenTelemetry.proj --configuration Release --no-restore -p:Deterministic=true -p:"BuildNumber=${env:GITHUB_RUN_NUMBER}" -p:RunningDotNetPack=true
- name: Sign DLLs with Cosign Keyless
shell: pwsh
@@ -64,7 +69,7 @@ jobs:
foreach ($projectFile in $projectFiles) {
$projectName = [System.IO.Path]::GetFileNameWithoutExtension($projectFile)
- Get-ChildItem -Path src/$projectName/bin/Release/*/$projectName.dll -File | ForEach-Object {
+ Get-ChildItem -Path artifacts/bin/$projectName/release_*/$projectName.dll -File | ForEach-Object {
$fileFullPath = $_.FullName
Write-Host "Signing $fileFullPath"
@@ -73,25 +78,31 @@ jobs:
}
- name: dotnet pack
- run: dotnet pack ./build/OpenTelemetry.proj --configuration Release --no-restore --no-build -p:PackTag=${{ github.ref_type == 'tag' && github.ref_name || '' }}
+ shell: pwsh
+ env:
+ PACK_TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
+ run: dotnet pack ./build/OpenTelemetry.proj --configuration Release --no-restore --no-build -p:"PackTag=${env:PACK_TAG}"
- name: Publish Artifacts
id: upload-artifacts
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ github.ref_name }}-packages
- path: 'src/**/*.*nupkg'
+ path: ./artifacts/package/release
+ if-no-files-found: error
- name: Publish MyGet
+ working-directory: ./artifacts/package/release
env:
MYGET_TOKEN_EXISTS: ${{ secrets.MYGET_TOKEN != '' }}
+ API_KEY: ${{ secrets.MYGET_TOKEN }}
+ SOURCE: https://www.myget.org/F/opentelemetry/api/v2/package
if: env.MYGET_TOKEN_EXISTS == 'true' # Skip MyGet publish if run on a fork without the secret
- run: |
- nuget setApiKey ${{ secrets.MYGET_TOKEN }} -Source https://www.myget.org/F/opentelemetry/api/v2/package
- nuget push src/**/*.nupkg -Source https://www.myget.org/F/opentelemetry/api/v2/package
+ shell: pwsh
+ run: dotnet nuget push *.nupkg --api-key ${env:API_KEY} --skip-duplicate --source ${env:SOURCE}
post-build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
needs:
- automation
@@ -99,45 +110,55 @@ jobs:
if: needs.automation.outputs.enabled && github.event_name == 'push'
- env:
- GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }}
-
steps:
- - name: check out code
- uses: actions/checkout@v4
+ - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
+ id: otelbot-token
with:
- token: ${{ secrets[needs.automation.outputs.token-secret-name] }}
+ app-id: ${{ vars.OTELBOT_DOTNET_APP_ID }}
+ private-key: ${{ secrets.OTELBOT_DOTNET_PRIVATE_KEY }}
+
+ - name: Check out code
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ with:
+ token: ${{ steps.otelbot-token.outputs.token }}
- name: Download Artifacts
+ env:
+ ARTIFACT_ID: ${{ needs.build-pack-publish.outputs.artifact-id }}
+ ARTIFACT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl \
-H "Accept: application/vnd.github+json" \
- -H "Authorization: token ${{ github.token }}" \
+ -H "Authorization: token ${ARTIFACT_TOKEN}" \
-L \
- -o '${{ github.workspace }}/artifacts/${{ github.ref_name }}-packages.zip' \
+ -o "${GITHUB_WORKSPACE}/artifacts/${GITHUB_REF_NAME}-packages.zip" \
--create-dirs \
- "https://api.github.com/repos/${{ github.repository }}/actions/artifacts/${{ needs.build-pack-publish.outputs.artifact-id }}/zip"
+ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/artifacts/${ARTIFACT_ID}/zip"
- name: Create GitHub Release draft
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
run: |
Import-Module .\build\scripts\post-release.psm1
CreateDraftRelease `
- -gitRepository '${{ github.repository }}' `
- -tag '${{ github.ref_name }}' `
- -releaseFiles '${{ github.workspace }}/artifacts/${{ github.ref_name }}-packages.zip#Packages'
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -tag ${env:GITHUB_REF_NAME} `
+ -releaseFiles "${env:GITHUB_WORKSPACE}/artifacts/${env:GITHUB_REF_NAME}-packages.zip"
- name: Post notice when packages are ready
shell: pwsh
+ env:
+ GH_TOKEN: ${{ steps.otelbot-token.outputs.token }}
+ EXPECTED_PR_AUTHOR_USER_NAME: ${{ needs.automation.outputs.application-name }}
+ PACKAGES_URL: ${{ needs.build-pack-publish.outputs.artifact-url }}
run: |
Import-Module .\build\scripts\post-release.psm1
TryPostPackagesReadyNoticeOnPrepareReleasePullRequest `
- -gitRepository '${{ github.repository }}' `
- -tag '${{ github.ref_name }}' `
- -tagSha '${{ github.sha }}' `
- -packagesUrl '${{ needs.build-pack-publish.outputs.artifact-url }}' `
- -botUserName '${{ needs.automation.outputs.username }}'
-
-
+ -gitRepository ${env:GITHUB_REPOSITORY} `
+ -tag ${env:GITHUB_REF_NAME} `
+ -tagSha ${env:GITHUB_SHA} `
+ -packagesUrl ${env:PACKAGES_URL} `
+ -expectedPrAuthorUserName ${env:EXPECTED_PR_AUTHOR_USER_NAME}
diff --git a/.github/workflows/sanitycheck.yml b/.github/workflows/sanitycheck.yml
index 335d389a9c7..54f89668af6 100644
--- a/.github/workflows/sanitycheck.yml
+++ b/.github/workflows/sanitycheck.yml
@@ -5,13 +5,16 @@ name: Lint - Spelling & Encoding
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-misspell:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: install misspell
run: |
@@ -22,11 +25,11 @@ jobs:
run: ./bin/misspell -error .
run-sanitycheck:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: detect non-ASCII encoding and trailing space
run: python3 ./build/scripts/sanitycheck.py
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index da30232dc9c..16b246d66aa 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -6,11 +6,17 @@ on:
schedule:
- cron: "12 3 * * *" # arbitrary time not to DDOS GitHub
+permissions:
+ contents: read
+
jobs:
stale:
- runs-on: ubuntu-22.04
+ permissions:
+ issues: write # for actions/stale to close stale issues
+ pull-requests: write # for actions/stale to close stale PRs
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/stale@v9
+ - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
with:
stale-issue-message: 'This issue was marked stale due to lack of activity and will be closed in 7 days. Commenting will instruct the bot to automatically remove the label. This bot runs once per day.'
close-issue-message: 'Closed as inactive. Feel free to reopen if this issue is still a concern.'
@@ -22,4 +28,5 @@ jobs:
days-before-pr-close: 7
days-before-issue-close: 7
exempt-all-issue-milestones: true
- exempt-issue-labels: needs-triage
+ exempt-issue-labels: 'keep-open,needs-triage'
+ exempt-pr-labels: 'keep-open'
diff --git a/.github/workflows/survey-on-merged-pr.yml b/.github/workflows/survey-on-merged-pr.yml
new file mode 100644
index 00000000000..3b3c80be6c5
--- /dev/null
+++ b/.github/workflows/survey-on-merged-pr.yml
@@ -0,0 +1,29 @@
+name: Survey on Merged PR by Non-Member
+
+on:
+ pull_request_target:
+ branches: [main]
+ types: [closed]
+
+permissions: {}
+
+jobs:
+ comment-on-pr:
+ name: Add survey to PR if author is not a member
+ permissions:
+ pull-requests: write
+ runs-on: ubuntu-latest
+ # Only run for merged pull requests by non-members users (i.e. not bots)
+ if: |
+ github.event.pull_request.merged &&
+ github.event.pull_request.user.type == 'User' &&
+ contains(fromJson('["CONTRIBUTOR", "FIRST_TIME_CONTRIBUTOR", "FIRST_TIMER"]'), github.event.pull_request.author_association)
+ steps:
+ - name: Add comment
+ run: |
+ gh pr comment "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --body "Thank you for your contribution @${USERNAME}! :tada: We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this [survey](${SURVEY_URL})."
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ SURVEY_URL: https://docs.google.com/forms/d/e/1FAIpQLSf2FfCsW-DimeWzdQgfl0KDzT2UEAqu69_f7F2BVPSxVae1cQ/viewform?entry.1540511742=${{ github.repository }}
+ USERNAME: ${{ github.event.pull_request.user.login }}
diff --git a/.github/workflows/verifyaotcompat.yml b/.github/workflows/verifyaotcompat.yml
index b85eaf5ca2d..affabafaf72 100644
--- a/.github/workflows/verifyaotcompat.yml
+++ b/.github/workflows/verifyaotcompat.yml
@@ -5,6 +5,9 @@ name: Publish & Verify AOT Compatibility
on:
workflow_call:
+permissions:
+ contents: read
+
jobs:
run-verify-aot-compat:
@@ -16,10 +19,10 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup dotnet
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
- name: publish AOT testApp, assert static analysis warning count, and run the app
shell: pwsh
diff --git a/.gitignore b/.gitignore
index e06229e460f..851ee867e69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@
x64/
x86/
bld/
+[Aa]rtifacts/
[Bb]in/
[Oo]bj/
[Ll]og/
@@ -351,3 +352,6 @@ tempo-data/
# Coyote Rewrite Files
rewrite.coyote.json
+
+# Test results
+TestResults/
diff --git a/.markdownlint.yaml b/.markdownlint.yaml
index c388b69fd5f..6efb6aeb05c 100644
--- a/.markdownlint.yaml
+++ b/.markdownlint.yaml
@@ -10,3 +10,6 @@ MD013:
MD033:
# Allowed elements
allowed_elements: [ 'details', 'summary' ]
+
+# MD059/link-text-should-be-descriptive : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md059.md
+MD059: false
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 02f8d2dd573..9ca8868d46a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,10 +14,33 @@ Anyone may contribute but there are benefits of being a member of our community.
See the [community membership
document](https://github.com/open-telemetry/community/blob/main/community-membership.md)
on how to become a
-[**Member**](https://github.com/open-telemetry/community/blob/main/community-membership.md#member),
-[**Approver**](https://github.com/open-telemetry/community/blob/main/community-membership.md#approver)
+[**Member**](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#member),
+[**Triager**](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager),
+[**Approver**](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver),
and
-[**Maintainer**](https://github.com/open-telemetry/community/blob/main/community-membership.md#maintainer).
+[**Maintainer**](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer).
+
+## Give feedback
+
+We are always looking for your feedback.
+
+You can do this by [submitting a GitHub issue](https://github.com/open-telemetry/opentelemetry-dotnet/issues/new).
+
+You may also prefer writing on [#otel-dotnet Slack channel](https://cloud-native.slack.com/archives/C01N3BC2W7Q).
+
+### Report a bug
+
+Reporting bugs is an important contribution. Please make sure to include:
+
+* Expected and actual behavior;
+* OpenTelemetry, OS, and .NET versions you are using;
+* Steps to reproduce;
+* [Minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example).
+
+### Request a feature
+
+If you would like to work on something that is not listed as an issue
+(e.g. a new feature or enhancement) please create an issue and describe your proposal.
## Find a buddy and get started quickly
@@ -300,9 +323,6 @@ types](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-type
* Pass [static
analysis](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/overview).
- New projects MUST enable static analysis by specifying
- `latest-all` in the project file (`.csproj`).
-
> [!NOTE]
> There are other project-level features enabled automatically via
[Common.props](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/build/Common.props)
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000000..e157295e687
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+ $([System.IO.Path]::Combine('$(MSBuildThisFileDirectory)', 'artifacts'))
+ true
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 70564887f83..0e3f4ce78a8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -2,18 +2,7 @@
true
- 1.11.0
-
-
- 9.0.0
-
-
- 8.0.0
- 8.0.5
+ 1.12.0
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
@@ -85,9 +62,10 @@
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
+
diff --git a/NuGet.config b/NuGet.config
index ca17f1b8088..ec945cd0620 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -3,19 +3,14 @@
-
-
-
-
-
diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln
index a072b792a9b..0384bcce8aa 100644
--- a/OpenTelemetry.sln
+++ b/OpenTelemetry.sln
@@ -254,9 +254,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A49299
src\Shared\ExceptionExtensions.cs = src\Shared\ExceptionExtensions.cs
src\Shared\Guard.cs = src\Shared\Guard.cs
src\Shared\MathHelper.cs = src\Shared\MathHelper.cs
- src\Shared\PeerServiceResolver.cs = src\Shared\PeerServiceResolver.cs
src\Shared\PeriodicExportingMetricReaderHelper.cs = src\Shared\PeriodicExportingMetricReaderHelper.cs
- src\Shared\PooledList.cs = src\Shared\PooledList.cs
src\Shared\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs
src\Shared\SemanticConventions.cs = src\Shared\SemanticConventions.cs
src\Shared\SpanAttributeConstants.cs = src\Shared\SpanAttributeConstants.cs
@@ -280,6 +278,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Options", "Options", "{4949
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shims", "Shims", "{A0CB9A10-F22D-4E66-A449-74B3D0361A9C}"
ProjectSection(SolutionItems) = preProject
+ src\Shared\Shims\ExperimentalAttribute.cs = src\Shared\Shims\ExperimentalAttribute.cs
src\Shared\Shims\IsExternalInit.cs = src\Shared\Shims\IsExternalInit.cs
src\Shared\Shims\Lock.cs = src\Shared\Shims\Lock.cs
src\Shared\Shims\NullableAttributes.cs = src\Shared\Shims\NullableAttributes.cs
diff --git a/README.md b/README.md
index 7da40f69cd9..303f2ce3f79 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,11 @@
[](https://www.nuget.org/profiles/OpenTelemetry)
[](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/ci.yml)
+[](https://scorecard.dev/viewer/?uri=github.com/open-telemetry/opentelemetry-dotnet)
+[](https://www.bestpractices.dev/projects/10017)
+[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fopen-telemetry%2Fopentelemetry-dotnet?ref=badge_shield&issueType=license)
+[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fopen-telemetry%2Fopentelemetry-dotnet?ref=badge_shield&issueType=security)
+
The .NET [OpenTelemetry](https://opentelemetry.io/) implementation.
@@ -231,26 +236,38 @@ regardless of your experience level. Whether you're a seasoned OpenTelemetry
developer, just starting your journey, or simply curious about the work we do,
you're more than welcome to participate!
-[Maintainers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer)
-([@open-telemetry/dotnet-maintainers](https://github.com/orgs/open-telemetry/teams/dotnet-maintainers)):
+### Maintainers
* [Alan West](https://github.com/alanwest), New Relic
+* [Piotr Kiełkowicz](https://github.com/Kielek), Splunk
+* [Rajkumar Rangaraj](https://github.com/rajkumar-rangaraj), Microsoft
+
+For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer).
+
+### Approvers
+
+* [Cijo Thomas](https://github.com/cijothomas), Microsoft
+* [Martin Costello](https://github.com/martincostello), Grafana Labs
* [Mikel Blanchard](https://github.com/CodeBlanch), Microsoft
-[Emeritus Maintainers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager):
+For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).
+
+### Triagers
+
+* [Martin Thwaites](https://github.com/martinjt), Honeycomb
+* [Timothy "Mothra" Lee](https://github.com/TimothyMothra)
+
+For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager).
+
+### Emeritus Maintainers
* [Mike Goldsmith](https://github.com/MikeGoldsmith)
* [Sergey Kanzhelev](https://github.com/SergeyKanzhelev)
* [Utkarsh Umesan Pillai](https://github.com/utpilla)
-[Approvers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver)
-([@open-telemetry/dotnet-approvers](https://github.com/orgs/open-telemetry/teams/dotnet-approvers)):
+For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
-* [Cijo Thomas](https://github.com/cijothomas), Microsoft
-* [Piotr Kiełkowicz](https://github.com/Kielek), Splunk
-* [Rajkumar Rangaraj](https://github.com/rajkumar-rangaraj), Microsoft
-
-[Emeritus Approvers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager):
+### Emeritus Approvers
* [Bruno Garcia](https://github.com/bruno-garcia)
* [Eddy Nakamura](https://github.com/eddynaka)
@@ -260,16 +277,14 @@ you're more than welcome to participate!
* [Robert Pająk](https://github.com/pellared)
* [Vishwesh Bankwar](https://github.com/vishweshbankwar)
-[Triagers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager)
-([@open-telemetry/dotnet-triagers](https://github.com/orgs/open-telemetry/teams/dotnet-triagers)):
-
-* [Martin Thwaites](https://github.com/martinjt), Honeycomb
-* [Timothy "Mothra" Lee](https://github.com/TimothyMothra), Microsoft
+For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
-[Emeritus Triagers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager):
+### Emeritus Triagers
* [Victor Lu](https://github.com/victlu)
+For more information about the emeritus role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
+
### Thanks to all the people who have contributed
[](https://github.com/open-telemetry/opentelemetry-dotnet/graphs/contributors)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 2d001c4ca8b..3a26d50aec8 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -4,13 +4,39 @@ This file contains highlights and announcements covering all components.
For more details see `CHANGELOG.md` files maintained in the root source
directory of each individual package.
+## 1.13.0
+
+Release details: [1.13.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.13.0)
+
+* gRPC calls to export traces, logs, and metrics using `OtlpExportProtocol.Grpc`
+ now set the `TE=trailers` HTTP request header to improve interoperability.
+* `EventName` is now exported by default as `EventName` instead of
+ `logrecord.event.name` when specified through `ILogger` or the experimental
+ log bridge API.
+
+## 1.12.0
+
+Release details: [1.12.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.12.0)
+
+* **Breaking Change**: `OpenTelemetry.Exporter.OpenTelemetryProtocol` now
+ defaults to using OTLP/HTTP instead of OTLP/gRPC when targeting .NET Framework
+ and .NET Standard. This change may cause telemetry export to fail unless
+ appropriate adjustments are made. Explicitly setting OTLP/gRPC may result in a
+ `NotSupportedException` unless further configuration is applied. See
+ [#6209](https://github.com/open-telemetry/opentelemetry-dotnet/issues/6209) for
+ full details and mitigation guidance. [#6229](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6229)
+
## 1.11.1
+Release details: [1.11.1](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.11.1)
+
* Fixed a bug preventing `OpenTelemetry.Exporter.OpenTelemetryProtocol` from
exporting telemetry on .NET Framework.
## 1.11.0
+Release details: [1.11.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.11.0)
+
* `OpenTelemetry.Exporter.OpenTelemetryProtocol` no longer depends on the
`Google.Protobuf`, `Grpc`, or `Grpc.Net.Client` packages. Serialization and
transmission of outgoing data is now performed manually to improve the overall
@@ -18,6 +44,8 @@ directory of each individual package.
## 1.10.0
+Release details: [1.10.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.10.0)
+
* Bumped the package versions of `System.Diagnostic.DiagnosticSource` and other
Microsoft.Extensions.* packages to `9.0.0`.
@@ -62,6 +90,8 @@ directory of each individual package.
## 1.9.0
+Release details: [1.9.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.9.0)
+
* `Exemplars` are now part of the stable API! For details see: [customizing
exemplars
collection](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#exemplars).
@@ -72,6 +102,8 @@ directory of each individual package.
## 1.8.0
+Release details: [1.8.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.8.0)
+
* `TracerProvider` sampler can now be configured via the `OTEL_TRACES_SAMPLER` &
`OTEL_TRACES_SAMPLER_ARG` envvars.
@@ -84,6 +116,8 @@ directory of each individual package.
## 1.7.0
+Release details: [1.7.0](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.7.0)
+
* Bumped the package versions of System.Diagnostic.DiagnosticSource and other
Microsoft.Extensions.* packages to `8.0.0`.
diff --git a/build/Common.nonprod.props b/build/Common.nonprod.props
index ce99895ccba..d5a959fd253 100644
--- a/build/Common.nonprod.props
+++ b/build/Common.nonprod.props
@@ -2,7 +2,7 @@
- $(NoWarn),1574,1591
+ $(NoWarn),CS1574,CS1591
false
$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'OpenTelemetry.sln'))\build\OpenTelemetry.test.ruleset
@@ -21,6 +21,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/Common.prod.props b/build/Common.prod.props
index 832306e1405..5a13bf20617 100644
--- a/build/Common.prod.props
+++ b/build/Common.prod.props
@@ -18,7 +18,6 @@
https://opentelemetry.io
OpenTelemetry Authors
Copyright The OpenTelemetry Authors
- $(Build_ArtifactStagingDirectory)
true
snupkg
Apache-2.0
@@ -36,16 +35,8 @@
true
-
-
-
-
-
-
@@ -185,10 +176,13 @@
+
+
+
$(NoWarn);OTEL1000;OTEL1001;OTEL1002;OTEL1004
-
-
+ latest-All
+
+
+
+ 002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898
diff --git a/build/RELEASING.md b/build/RELEASING.md
index 28fdc9a078e..bb430aaf9e8 100644
--- a/build/RELEASING.md
+++ b/build/RELEASING.md
@@ -80,11 +80,21 @@ Maintainers (admins) are needed to merge PRs and for the push to NuGet.**
for the projects being released.
- 5. :stop_sign: Wait for the [Build, pack, and publish to
+ 6. :stop_sign: Wait for the [Build, pack, and publish to
MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml)
workflow to complete. When complete a trigger will automatically add a
comment on the PR opened by [Prepare for a
@@ -187,14 +197,14 @@ Maintainers (admins) are needed to merge PRs and for the push to NuGet.**
draft Release and click `Publish release`.
- 6. If a new stable version of the core packages was released, a PR should have
+ 7. If a new stable version of the core packages was released, a PR should have
been automatically created by the [Complete
release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/post-release.yml)
workflow to update the `OTelLatestStableVer` property in
`Directory.Packages.props` to the just released stable version. Merge that
PR once the build passes (this requires the packages be available on NuGet).
- 7. The [Complete
+ 8. The [Complete
release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/post-release.yml)
workflow should have invoked the [Core version
update](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/actions/workflows/core-version-update.yml)
@@ -203,7 +213,6 @@ Maintainers (admins) are needed to merge PRs and for the push to NuGet.**
repository which opens a PR to update dependencies. Verify this PR was
opened successfully.
- 8. For stable releases, update [Release Notes](../RELEASENOTES.md) with any big
- or interesting new features and then post an announcement in the [Slack
- channel](https://cloud-native.slack.com/archives/C01N3BC2W7Q) with the same
- information.
+ 9. For stable releases post an announcement in the [Slack
+ channel](https://cloud-native.slack.com/archives/C01N3BC2W7Q) announcing the
+ release and link to the release notes.
diff --git a/build/docker-compose.net8.0.yml b/build/docker-compose.net8.0.yml
index 22787bff66e..b93fc24655e 100644
--- a/build/docker-compose.net8.0.yml
+++ b/build/docker-compose.net8.0.yml
@@ -1,5 +1,3 @@
-version: '3.7'
-
services:
tests:
build:
diff --git a/build/docker-compose.net9.0.yml b/build/docker-compose.net9.0.yml
index 29663b3246c..94176258b24 100644
--- a/build/docker-compose.net9.0.yml
+++ b/build/docker-compose.net9.0.yml
@@ -1,5 +1,3 @@
-version: '3.7'
-
services:
tests:
build:
diff --git a/build/scripts/post-release.psm1 b/build/scripts/post-release.psm1
index ae20f939e29..6edbe955474 100644
--- a/build/scripts/post-release.psm1
+++ b/build/scripts/post-release.psm1
@@ -125,7 +125,7 @@ function TryPostPackagesReadyNoticeOnPrepareReleasePullRequest {
[Parameter(Mandatory=$true)][string]$tag,
[Parameter(Mandatory=$true)][string]$tagSha,
[Parameter(Mandatory=$true)][string]$packagesUrl,
- [Parameter(Mandatory=$true)][string]$botUserName
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName
)
$prListResponse = gh pr list --search $tagSha --state merged --json number,author,title,comments | ConvertFrom-Json
@@ -138,7 +138,7 @@ function TryPostPackagesReadyNoticeOnPrepareReleasePullRequest {
foreach ($pr in $prListResponse)
{
- if ($pr.author.login -ne $botUserName -or $pr.title -ne "[release] Prepare release $tag")
+ if ($pr.author.login -ne $expectedPrAuthorUserName -or $pr.title -ne "[release] Prepare release $tag")
{
continue
}
@@ -146,7 +146,7 @@ function TryPostPackagesReadyNoticeOnPrepareReleasePullRequest {
$foundComment = $false
foreach ($comment in $pr.comments)
{
- if ($comment.author.login -eq $botUserName -and $comment.body.StartsWith("I just pushed the [$tag]"))
+ if ($comment.author.login -eq $expectedPrAuthorUserName -and $comment.body.StartsWith("I just pushed the [$tag]"))
{
$foundComment = $true
break
@@ -180,15 +180,15 @@ function PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullReques
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
[Parameter(Mandatory=$true)][string]$pullRequestNumber,
- [Parameter(Mandatory=$true)][string]$botUserName,
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName,
[Parameter(Mandatory=$true)][string]$commentUserName,
[Parameter(Mandatory=$true)][string]$artifactDownloadPath,
- [Parameter(Mandatory=$true)][string]$pushToNuget
+ [Parameter(Mandatory=$true)][bool]$pushToNuget
)
$prViewResponse = gh pr view $pullRequestNumber --json author,title,comments | ConvertFrom-Json
- if ($prViewResponse.author.login -ne $botUserName)
+ if ($prViewResponse.author.login -ne $expectedPrAuthorUserName)
{
throw 'PR author was unexpected'
}
@@ -213,7 +213,7 @@ function PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullReques
$packagesUrl = ''
foreach ($comment in $prViewResponse.comments)
{
- if ($comment.author.login -eq $botUserName -and $comment.body.StartsWith("The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available:"))
+ if ($comment.author.login -eq $expectedPrAuthorUserName -and $comment.body.StartsWith("The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available:"))
{
$foundComment = $true
break
@@ -231,12 +231,12 @@ function PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullReques
Expand-Archive -LiteralPath "$artifactDownloadPath/$tag-packages.zip" -DestinationPath "$artifactDownloadPath\"
- if ($pushToNuget -eq 'true')
+ if ($pushToNuget)
{
gh pr comment $pullRequestNumber `
--body "I am uploading the packages for ``$tag`` to NuGet and then I will publish the release."
- nuget push "$artifactDownloadPath/**/*.nupkg" -Source https://api.nuget.org/v3/index.json -ApiKey "$env:NUGET_TOKEN" -SymbolApiKey "$env:NUGET_TOKEN"
+ dotnet nuget push "$artifactDownloadPath/**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key "$env:NUGET_TOKEN" --symbol-api-key "$env:NUGET_TOKEN"
if ($LASTEXITCODE -gt 0)
{
@@ -276,7 +276,7 @@ function CreateStableVersionUpdatePullRequest {
$version = $match.Groups[1].Value
- $branch="release/post-stable-${tag}-update"
+ $branch="otelbot/post-stable-${tag}-update"
if ([string]::IsNullOrEmpty($gitUserName) -eq $false)
{
@@ -539,7 +539,7 @@ Export-ModuleMember -Function InvokeCoreVersionUpdateWorkflowInRemoteRepository
function TryPostReleasePublishedNoticeOnPrepareReleasePullRequest {
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
- [Parameter(Mandatory=$true)][string]$botUserName,
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName,
[Parameter(Mandatory=$true)][string]$tag
)
@@ -559,7 +559,7 @@ function TryPostReleasePublishedNoticeOnPrepareReleasePullRequest {
foreach ($pr in $prListResponse)
{
- if ($pr.author.login -ne $botUserName -or $pr.title -ne "[release] Prepare release $tag")
+ if ($pr.author.login -ne $expectedPrAuthorUserName -or $pr.title -ne "[release] Prepare release $tag")
{
continue
}
@@ -567,7 +567,7 @@ function TryPostReleasePublishedNoticeOnPrepareReleasePullRequest {
$foundComment = $false
foreach ($comment in $pr.comments)
{
- if ($comment.author.login -eq $botUserName -and $comment.body.StartsWith("The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available:"))
+ if ($comment.author.login -eq $expectedPrAuthorUserName -and $comment.body.StartsWith("The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available:"))
{
$foundComment = $true
break
diff --git a/build/scripts/prepare-release.psm1 b/build/scripts/prepare-release.psm1
index c1998e08149..4a6ee4678f4 100644
--- a/build/scripts/prepare-release.psm1
+++ b/build/scripts/prepare-release.psm1
@@ -17,7 +17,7 @@ function CreatePullRequestToUpdateChangelogsAndPublicApis {
$isPrerelease = $version -match '-alpha' -or $version -match '-beta' -or $version -match '-rc'
$tag="${minVerTagPrefix}${version}"
- $branch="release/prepare-${tag}-release"
+ $branch="otelbot/prepare-${tag}-release"
if ([string]::IsNullOrEmpty($gitUserName) -eq $false)
{
@@ -136,12 +136,12 @@ function LockPullRequestAndPostNoticeToCreateReleaseTag {
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
[Parameter(Mandatory=$true)][string]$pullRequestNumber,
- [Parameter(Mandatory=$true)][string]$botUserName
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName
)
$prViewResponse = gh pr view $pullRequestNumber --json mergeCommit,author,title | ConvertFrom-Json
- if ($prViewResponse.author.login -ne $botUserName)
+ if ($prViewResponse.author.login -ne $expectedPrAuthorUserName)
{
throw 'PR author was unexpected'
}
@@ -178,14 +178,14 @@ function CreateReleaseTagAndPostNoticeOnPullRequest {
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
[Parameter(Mandatory=$true)][string]$pullRequestNumber,
- [Parameter(Mandatory=$true)][string]$botUserName,
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName,
[Parameter()][string]$gitUserName,
[Parameter()][string]$gitUserEmail
)
$prViewResponse = gh pr view $pullRequestNumber --json mergeCommit,author,title | ConvertFrom-Json
- if ($prViewResponse.author.login -ne $botUserName)
+ if ($prViewResponse.author.login -ne $expectedPrAuthorUserName)
{
throw 'PR author was unexpected'
}
@@ -241,7 +241,7 @@ function UpdateChangelogReleaseDatesAndPostNoticeOnPullRequest {
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
[Parameter(Mandatory=$true)][string]$pullRequestNumber,
- [Parameter(Mandatory=$true)][string]$botUserName,
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName,
[Parameter(Mandatory=$true)][string]$commentUserName,
[Parameter()][string]$gitUserName,
[Parameter()][string]$gitUserEmail
@@ -249,7 +249,7 @@ function UpdateChangelogReleaseDatesAndPostNoticeOnPullRequest {
$prViewResponse = gh pr view $pullRequestNumber --json headRefName,author,title | ConvertFrom-Json
- if ($prViewResponse.author.login -ne $botUserName)
+ if ($prViewResponse.author.login -ne $expectedPrAuthorUserName)
{
throw 'PR author was unexpected'
}
@@ -344,7 +344,7 @@ function UpdateReleaseNotesAndPostNoticeOnPullRequest {
param(
[Parameter(Mandatory=$true)][string]$gitRepository,
[Parameter(Mandatory=$true)][string]$pullRequestNumber,
- [Parameter(Mandatory=$true)][string]$botUserName,
+ [Parameter(Mandatory=$true)][string]$expectedPrAuthorUserName,
[Parameter(Mandatory=$true)][string]$commentUserName,
[Parameter(Mandatory=$true)][string]$commentBody,
[Parameter()][string]$gitUserName,
@@ -353,7 +353,7 @@ function UpdateReleaseNotesAndPostNoticeOnPullRequest {
$prViewResponse = gh pr view $pullRequestNumber --json headRefName,author,title | ConvertFrom-Json
- if ($prViewResponse.author.login -ne $botUserName)
+ if ($prViewResponse.author.login -ne $expectedPrAuthorUserName)
{
throw 'PR author was unexpected'
}
@@ -420,6 +420,8 @@ function UpdateReleaseNotesAndPostNoticeOnPullRequest {
@"
## $version
+Release details: [$version](https://github.com/$gitRepository/releases/tag/$tagPrefix$version)
+
$content
##
diff --git a/build/scripts/test-aot-compatibility.ps1 b/build/scripts/test-aot-compatibility.ps1
index 895055a1b48..99def7badd5 100644
--- a/build/scripts/test-aot-compatibility.ps1
+++ b/build/scripts/test-aot-compatibility.ps1
@@ -24,10 +24,9 @@ if ($LastExitCode -ne 0)
Write-Host $publishOutput
}
-$runtime = $IsWindows ? "win-x64" : ($IsMacOS ? "macos-x64" : "linux-x64")
$app = $IsWindows ? "./OpenTelemetry.AotCompatibility.TestApp.exe" : "./OpenTelemetry.AotCompatibility.TestApp"
-Push-Location $rootDirectory/test/OpenTelemetry.AotCompatibility.TestApp/bin/Release/$targetNetFramework/$runtime
+Push-Location $rootDirectory/artifacts/publish/OpenTelemetry.AotCompatibility.TestApp/release_$targetNetFramework
Write-Host "Executing test App..."
$app
diff --git a/build/scripts/test-threadSafety.ps1 b/build/scripts/test-threadSafety.ps1
index 73cc1f27d27..6fbaba60e5c 100644
--- a/build/scripts/test-threadSafety.ps1
+++ b/build/scripts/test-threadSafety.ps1
@@ -1,22 +1,32 @@
param(
- [Parameter()][string]$coyoteVersion="1.7.10",
+ [Parameter()][string]$coyoteVersion="1.7.11",
[Parameter(Mandatory=$true)][string]$testProjectName,
[Parameter(Mandatory=$true)][string]$targetFramework,
[Parameter()][string]$categoryName="CoyoteConcurrencyTests",
[Parameter()][string]$configuration="Release"
)
+$ErrorActionPreference = "Stop"
+
$env:OTEL_RUN_COYOTE_TESTS = 'true'
$rootDirectory = Get-Location
Write-Host "Install Coyote CLI."
-dotnet tool install --global Microsoft.Coyote.CLI
+dotnet tool install --global Microsoft.Coyote.CLI --version $coyoteVersion
+
+if ($LASTEXITCODE -ne 0) {
+ throw "Microsoft.Coyote.CLI installation failed with exit code $LASTEXITCODE"
+}
Write-Host "Build $testProjectName project."
dotnet build "$rootDirectory/test/$testProjectName/$testProjectName.csproj" --configuration $configuration
-$artifactsPath = Join-Path $rootDirectory "test/$testProjectName/bin/$configuration/$targetFramework"
+if ($LASTEXITCODE -ne 0) {
+ throw "dotnet build failed with exit code $LASTEXITCODE"
+}
+
+$artifactsPath = Join-Path $rootDirectory "artifacts/bin/$testProjectName/$($configuration.ToLowerInvariant())_$targetFramework"
Write-Host "Generate Coyote rewriting options JSON file."
$assemblies = Get-ChildItem $artifactsPath -Filter OpenTelemetry*.dll | ForEach-Object {$_.Name}
@@ -29,6 +39,13 @@ $RewriteOptionsJson | ConvertTo-Json -Compress | Set-Content -Path "$rootDirecto
Write-Host "Run Coyote rewrite."
coyote rewrite "$rootDirectory/test/$testProjectName/rewrite.coyote.json"
+if ($LASTEXITCODE -ne 0) {
+ throw "coyote rewrite failed with exit code $LASTEXITCODE"
+}
+
Write-Host "Execute re-written binary."
dotnet test "$artifactsPath/$testProjectName.dll" --framework $targetFramework --filter CategoryName=$categoryName
+if ($LASTEXITCODE -ne 0) {
+ throw "dotnet test failed with exit code $LASTEXITCODE"
+}
diff --git a/docs/Directory.Build.props b/docs/Directory.Build.props
index 942eea5974c..9bc6a965577 100644
--- a/docs/Directory.Build.props
+++ b/docs/Directory.Build.props
@@ -1,8 +1,11 @@
+
Exe
$(TargetFrameworksForDocs)
+
+ false
diff --git a/docs/logs/complex-objects/FoodRecallNotice.cs b/docs/logs/complex-objects/FoodRecallNotice.cs
index 40ca59e3b8d..e771fc7adc4 100644
--- a/docs/logs/complex-objects/FoodRecallNotice.cs
+++ b/docs/logs/complex-objects/FoodRecallNotice.cs
@@ -1,7 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-public struct FoodRecallNotice
+namespace ComplexObjects;
+
+internal struct FoodRecallNotice
{
public string? BrandName { get; set; }
diff --git a/docs/logs/complex-objects/Program.cs b/docs/logs/complex-objects/Program.cs
index c09cfc5c772..d4aa278f89e 100644
--- a/docs/logs/complex-objects/Program.cs
+++ b/docs/logs/complex-objects/Program.cs
@@ -1,6 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+using ComplexObjects;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
diff --git a/docs/logs/correlation/Program.cs b/docs/logs/correlation/Program.cs
index b1a3284fbcc..6eee75f3657 100644
--- a/docs/logs/correlation/Program.cs
+++ b/docs/logs/correlation/Program.cs
@@ -7,7 +7,9 @@
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
-public class Program
+namespace Correlation;
+
+internal sealed class Program
{
private static readonly ActivitySource MyActivitySource = new("MyCompany.MyProduct.MyLibrary");
diff --git a/docs/logs/dedicated-pipeline/DedicatedLogging/DedicatedLoggingServiceCollectionExtensions.cs b/docs/logs/dedicated-pipeline/DedicatedLogging/DedicatedLoggingServiceCollectionExtensions.cs
index c9343185e21..6652c195586 100644
--- a/docs/logs/dedicated-pipeline/DedicatedLogging/DedicatedLoggingServiceCollectionExtensions.cs
+++ b/docs/logs/dedicated-pipeline/DedicatedLogging/DedicatedLoggingServiceCollectionExtensions.cs
@@ -6,7 +6,7 @@
namespace DedicatedLogging;
-public static class DedicatedLoggingServiceCollectionExtensions
+internal static class DedicatedLoggingServiceCollectionExtensions
{
public static IServiceCollection AddDedicatedLogging(
this IServiceCollection services,
diff --git a/docs/logs/dedicated-pipeline/DedicatedLogging/IDedicatedLogger.cs b/docs/logs/dedicated-pipeline/DedicatedLogging/IDedicatedLogger.cs
index ba09f27e271..975300a60e0 100644
--- a/docs/logs/dedicated-pipeline/DedicatedLogging/IDedicatedLogger.cs
+++ b/docs/logs/dedicated-pipeline/DedicatedLogging/IDedicatedLogger.cs
@@ -3,10 +3,10 @@
namespace DedicatedLogging;
-public interface IDedicatedLogger : ILogger
+internal interface IDedicatedLogger : ILogger
{
}
-public interface IDedicatedLogger : IDedicatedLogger
+internal interface IDedicatedLogger : IDedicatedLogger
{
}
diff --git a/docs/logs/dedicated-pipeline/dedicated-pipeline.csproj b/docs/logs/dedicated-pipeline/dedicated-pipeline.csproj
index cebc8460c42..6426d0f7ddd 100644
--- a/docs/logs/dedicated-pipeline/dedicated-pipeline.csproj
+++ b/docs/logs/dedicated-pipeline/dedicated-pipeline.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA1812;CA2213
+
+
diff --git a/docs/logs/extending-the-sdk/MyExporter.cs b/docs/logs/extending-the-sdk/MyExporter.cs
index 6093f57394b..25b2d9809e1 100644
--- a/docs/logs/extending-the-sdk/MyExporter.cs
+++ b/docs/logs/extending-the-sdk/MyExporter.cs
@@ -5,7 +5,7 @@
using OpenTelemetry;
using OpenTelemetry.Logs;
-internal class MyExporter : BaseExporter
+internal sealed class MyExporter : BaseExporter
{
private readonly string name;
@@ -59,6 +59,7 @@ protected override bool OnShutdown(int timeoutMilliseconds)
protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
diff --git a/docs/logs/extending-the-sdk/MyProcessor.cs b/docs/logs/extending-the-sdk/MyProcessor.cs
index 5fcd34e1d8d..739f9f6f232 100644
--- a/docs/logs/extending-the-sdk/MyProcessor.cs
+++ b/docs/logs/extending-the-sdk/MyProcessor.cs
@@ -4,7 +4,7 @@
using OpenTelemetry;
using OpenTelemetry.Logs;
-internal class MyProcessor : BaseProcessor
+internal sealed class MyProcessor : BaseProcessor
{
private readonly string name;
@@ -32,6 +32,7 @@ protected override bool OnShutdown(int timeoutMilliseconds)
protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
diff --git a/docs/logs/extending-the-sdk/Program.cs b/docs/logs/extending-the-sdk/Program.cs
index cada6ec1f5d..1de67683841 100644
--- a/docs/logs/extending-the-sdk/Program.cs
+++ b/docs/logs/extending-the-sdk/Program.cs
@@ -6,7 +6,7 @@
namespace ExtendingTheSdk;
-public class Program
+internal sealed class Program
{
public static void Main()
{
@@ -29,16 +29,16 @@ public static void Main()
// logger.LogInformation($"Hello from potato {0.99}.");
// structured log with template
- logger.LogInformation("Hello from {name} {price}.", "tomato", 2.99);
+ logger.LogInformation("Hello from {Name} {Price}.", "tomato", 2.99);
// structured log with strong type
- logger.LogInformation("{food}", new Food { Name = "artichoke", Price = 3.99 });
+ logger.LogInformation("{Food}", new Food { Name = "artichoke", Price = 3.99 });
// structured log with anonymous type
- logger.LogInformation("{food}", new { Name = "pumpkin", Price = 5.99 });
+ logger.LogInformation("{Food}", new { Name = "pumpkin", Price = 5.99 });
// structured log with general type
- logger.LogInformation("{food}", new Dictionary
+ logger.LogInformation("{Food}", new Dictionary
{
["Name"] = "truffle",
["Price"] = 299.99,
@@ -48,11 +48,11 @@ public static void Main()
using (logger.BeginScope("[operation]"))
using (logger.BeginScope("[hardware]"))
{
- logger.LogError("{name} is broken.", "refrigerator");
+ logger.LogError("{Name} is broken.", "refrigerator");
}
// message will be redacted by MyRedactionProcessor
- logger.LogInformation("OpenTelemetry {sensitiveString}.", "");
+ logger.LogInformation("OpenTelemetry {SensitiveString}.", "");
}
internal struct Food
diff --git a/docs/logs/extending-the-sdk/extending-the-sdk.csproj b/docs/logs/extending-the-sdk/extending-the-sdk.csproj
index 4d96c349671..e775de84b5d 100644
--- a/docs/logs/extending-the-sdk/extending-the-sdk.csproj
+++ b/docs/logs/extending-the-sdk/extending-the-sdk.csproj
@@ -1,4 +1,8 @@

+
+ $(NoWarn);CA2000;CA1848;CA1510;CA1305
+
+
diff --git a/docs/logs/redaction/redaction.csproj b/docs/logs/redaction/redaction.csproj
index 19aa9791432..f9c04fa7bff 100644
--- a/docs/logs/redaction/redaction.csproj
+++ b/docs/logs/redaction/redaction.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA2213;CA1812;CA1307
+
+
diff --git a/docs/metrics/README.md b/docs/metrics/README.md
index 91736713579..a51c76319bb 100644
--- a/docs/metrics/README.md
+++ b/docs/metrics/README.md
@@ -59,7 +59,7 @@ too frequently. `Meter` is fairly expensive and meant to be reused throughout
the application. For most applications, it can be modeled as static readonly
field (e.g. [Program.cs](./getting-started-console/Program.cs)) or singleton via
dependency injection (e.g.
-[Instrumentation.cs](../../examples/AspNetCore/Instrumentation.cs)).
+[InstrumentationSource.cs](../../examples/AspNetCore/InstrumentationSource.cs)).
:heavy_check_mark: You should use dot-separated
[UpperCamelCase](https://en.wikipedia.org/wiki/Camel_case) as the
@@ -97,7 +97,7 @@ frequently. Instruments are fairly expensive and meant to be reused throughout
the application. For most applications, instruments can be modeled as static
readonly fields (e.g. [Program.cs](./getting-started-console/Program.cs)) or
singleton via dependency injection (e.g.
-[Instrumentation.cs](../../examples/AspNetCore/Instrumentation.cs)).
+[InstrumentationSource.cs](../../examples/AspNetCore/InstrumentationSource.cs)).
:stop_sign: You should avoid invalid instrument names.
diff --git a/docs/metrics/customizing-the-sdk/Program.cs b/docs/metrics/customizing-the-sdk/Program.cs
index bbfd4bea98f..cdcb6dcbbaf 100644
--- a/docs/metrics/customizing-the-sdk/Program.cs
+++ b/docs/metrics/customizing-the-sdk/Program.cs
@@ -8,7 +8,7 @@
namespace CustomizingTheSdk;
-public class Program
+internal static class Program
{
private static readonly Meter Meter1 = new("CompanyA.ProductA.Library1", "1.0");
private static readonly Meter Meter2 = new("CompanyA.ProductB.Library2", "1.0");
@@ -29,19 +29,19 @@ public static void Main()
.AddView(instrumentName: "MyCounter", name: "MyCounterRenamed")
// Change Histogram boundaries using the Explicit Bucket Histogram aggregation.
- .AddView(instrumentName: "MyHistogram", new ExplicitBucketHistogramConfiguration() { Boundaries = new double[] { 10, 20 } })
+ .AddView(instrumentName: "MyHistogram", new ExplicitBucketHistogramConfiguration() { Boundaries = [10.0, 20.0] })
// Change Histogram to use the Base2 Exponential Bucket Histogram aggregation.
.AddView(instrumentName: "MyExponentialBucketHistogram", new Base2ExponentialBucketHistogramConfiguration())
// For the instrument "MyCounterCustomTags", aggregate with only the keys "tag1", "tag2".
- .AddView(instrumentName: "MyCounterCustomTags", new MetricStreamConfiguration() { TagKeys = new string[] { "tag1", "tag2" } })
+ .AddView(instrumentName: "MyCounterCustomTags", new MetricStreamConfiguration() { TagKeys = ["tag1", "tag2"] })
// Drop the instrument "MyCounterDrop".
.AddView(instrumentName: "MyCounterDrop", MetricStreamConfiguration.Drop)
// Configure the Explicit Bucket Histogram aggregation with custom boundaries and new name.
- .AddView(instrumentName: "histogramWithMultipleAggregations", new ExplicitBucketHistogramConfiguration() { Boundaries = new double[] { 10, 20 }, Name = "MyHistogramWithExplicitHistogram" })
+ .AddView(instrumentName: "histogramWithMultipleAggregations", new ExplicitBucketHistogramConfiguration() { Boundaries = [10.0, 20.0], Name = "MyHistogramWithExplicitHistogram" })
// Use Base2 Exponential Bucket Histogram aggregation and new name.
.AddView(instrumentName: "histogramWithMultipleAggregations", new Base2ExponentialBucketHistogramConfiguration() { Name = "MyHistogramWithBase2ExponentialBucketHistogram" })
diff --git a/docs/metrics/customizing-the-sdk/customizing-the-sdk.csproj b/docs/metrics/customizing-the-sdk/customizing-the-sdk.csproj
index 19aa9791432..97876c06b9c 100644
--- a/docs/metrics/customizing-the-sdk/customizing-the-sdk.csproj
+++ b/docs/metrics/customizing-the-sdk/customizing-the-sdk.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394
+
+
diff --git a/docs/metrics/exemplars/Program.cs b/docs/metrics/exemplars/Program.cs
index cd63ac77ecc..d54e0ff6a2b 100644
--- a/docs/metrics/exemplars/Program.cs
+++ b/docs/metrics/exemplars/Program.cs
@@ -8,7 +8,9 @@
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
-public class Program
+namespace Exemplars;
+
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("OpenTelemetry.Demo.Exemplar");
private static readonly Meter MyMeter = new("OpenTelemetry.Demo.Exemplar");
diff --git a/docs/metrics/exemplars/README.md b/docs/metrics/exemplars/README.md
index 89f88866560..1f532c2ff0e 100644
--- a/docs/metrics/exemplars/README.md
+++ b/docs/metrics/exemplars/README.md
@@ -58,7 +58,7 @@ and
enabled:
```sh
-./prometheus --enable-feature=exemplar-storage --enable-feature=otlp-write-receiver
+./prometheus --enable-feature=exemplar-storage --web.enable-otlp-receiver
```
## Install and configure Grafana
diff --git a/docs/metrics/exemplars/exemplars.csproj b/docs/metrics/exemplars/exemplars.csproj
index cce12eec60d..686278e7a42 100644
--- a/docs/metrics/exemplars/exemplars.csproj
+++ b/docs/metrics/exemplars/exemplars.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394
+
+
diff --git a/docs/metrics/extending-the-sdk/MyExporter.cs b/docs/metrics/extending-the-sdk/MyExporter.cs
index 228d3e0c342..376fa2f5b97 100644
--- a/docs/metrics/extending-the-sdk/MyExporter.cs
+++ b/docs/metrics/extending-the-sdk/MyExporter.cs
@@ -5,7 +5,7 @@
using OpenTelemetry;
using OpenTelemetry.Metrics;
-internal class MyExporter : BaseExporter
+internal sealed class MyExporter : BaseExporter
{
private readonly string name;
@@ -28,7 +28,7 @@ public override ExportResult Export(in Batch batch)
sb.Append(", ");
}
- sb.Append($"{metric.Name}");
+ sb.Append(metric.Name);
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
{
@@ -52,6 +52,7 @@ protected override bool OnShutdown(int timeoutMilliseconds)
protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
diff --git a/docs/metrics/extending-the-sdk/Program.cs b/docs/metrics/extending-the-sdk/Program.cs
index a2952062261..0a7cf838cd1 100644
--- a/docs/metrics/extending-the-sdk/Program.cs
+++ b/docs/metrics/extending-the-sdk/Program.cs
@@ -8,7 +8,7 @@
namespace ExtendingTheSdk;
-public class Program
+internal static class Program
{
private static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0");
private static readonly Counter MyFruitCounter = MyMeter.CreateCounter("MyFruitCounter");
diff --git a/docs/metrics/extending-the-sdk/extending-the-sdk.csproj b/docs/metrics/extending-the-sdk/extending-the-sdk.csproj
index 4d96c349671..98a53a13c5a 100644
--- a/docs/metrics/extending-the-sdk/extending-the-sdk.csproj
+++ b/docs/metrics/extending-the-sdk/extending-the-sdk.csproj
@@ -1,4 +1,8 @@

+
+ $(NoWarn);CA2000;CA1510;CA1305
+
+
diff --git a/docs/metrics/getting-started-console/Program.cs b/docs/metrics/getting-started-console/Program.cs
index 89425c6d41c..dbc42339877 100644
--- a/docs/metrics/getting-started-console/Program.cs
+++ b/docs/metrics/getting-started-console/Program.cs
@@ -5,7 +5,9 @@
using OpenTelemetry;
using OpenTelemetry.Metrics;
-public class Program
+namespace GettingStartedConsole;
+
+internal static class Program
{
private static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0");
private static readonly Counter MyFruitCounter = MyMeter.CreateCounter("MyFruitCounter");
diff --git a/docs/metrics/getting-started-prometheus-grafana/Program.cs b/docs/metrics/getting-started-prometheus-grafana/Program.cs
index 0fd2437eefd..848116636c0 100644
--- a/docs/metrics/getting-started-prometheus-grafana/Program.cs
+++ b/docs/metrics/getting-started-prometheus-grafana/Program.cs
@@ -6,7 +6,9 @@
using OpenTelemetry.Exporter;
using OpenTelemetry.Metrics;
-public class Program
+namespace GettingStartedPrometheusGrafana;
+
+internal static class Program
{
private static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0");
private static readonly Counter MyFruitCounter = MyMeter.CreateCounter("MyFruitCounter");
diff --git a/docs/metrics/getting-started-prometheus-grafana/README.md b/docs/metrics/getting-started-prometheus-grafana/README.md
index dfc3e09c1a3..75a09cb1226 100644
--- a/docs/metrics/getting-started-prometheus-grafana/README.md
+++ b/docs/metrics/getting-started-prometheus-grafana/README.md
@@ -88,7 +88,7 @@ access. Run the `prometheus(.exe)` server executable with feature flag
enabled:
```sh
-./prometheus --enable-feature=otlp-write-receiver
+./prometheus --web.enable-otlp-receiver
```
### View results in Prometheus
diff --git a/docs/metrics/learning-more-instruments/Program.cs b/docs/metrics/learning-more-instruments/Program.cs
index c887e281461..9acbcac5639 100644
--- a/docs/metrics/learning-more-instruments/Program.cs
+++ b/docs/metrics/learning-more-instruments/Program.cs
@@ -8,7 +8,7 @@
namespace LearningMoreInstruments;
-public class Program
+internal static class Program
{
private static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0");
private static readonly Histogram MyHistogram = MyMeter.CreateHistogram("MyHistogram");
diff --git a/docs/metrics/learning-more-instruments/learning-more-instruments.csproj b/docs/metrics/learning-more-instruments/learning-more-instruments.csproj
index 9f5b6b79bc3..001e041f0f7 100644
--- a/docs/metrics/learning-more-instruments/learning-more-instruments.csproj
+++ b/docs/metrics/learning-more-instruments/learning-more-instruments.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394
+
+
diff --git a/docs/resources/extending-the-sdk/LoggerExtensions.cs b/docs/resources/extending-the-sdk/LoggerExtensions.cs
new file mode 100644
index 00000000000..a57502fc0f8
--- /dev/null
+++ b/docs/resources/extending-the-sdk/LoggerExtensions.cs
@@ -0,0 +1,12 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace ExtendingTheSdk;
+
+internal static partial class LoggerExtensions
+{
+ [LoggerMessage(LogLevel.Information, "Hello from {Name} {Price}")]
+ public static partial void HelloFrom(this ILogger logger, string name, double price);
+}
diff --git a/docs/resources/extending-the-sdk/MyResourceDetector.cs b/docs/resources/extending-the-sdk/MyResourceDetector.cs
index df40d9b3302..978e4a7edb6 100644
--- a/docs/resources/extending-the-sdk/MyResourceDetector.cs
+++ b/docs/resources/extending-the-sdk/MyResourceDetector.cs
@@ -3,7 +3,7 @@
using OpenTelemetry.Resources;
-internal class MyResourceDetector : IResourceDetector
+internal sealed class MyResourceDetector : IResourceDetector
{
public Resource Detect()
{
diff --git a/docs/resources/extending-the-sdk/Program.cs b/docs/resources/extending-the-sdk/Program.cs
index 849bdcc182b..6877d0c5cc7 100644
--- a/docs/resources/extending-the-sdk/Program.cs
+++ b/docs/resources/extending-the-sdk/Program.cs
@@ -11,7 +11,7 @@
namespace ExtendingTheSdk;
-public class Program
+internal static class Program
{
private static readonly ActivitySource DemoSource = new("OTel.Demo");
private static readonly Meter MeterDemoSource = new("OTel.Demo");
@@ -55,7 +55,6 @@ public static void Main()
}
var logger = loggerFactory.CreateLogger("OTel.Demo");
- logger
- .LogInformation("Hello from {Name} {Price}", "tomato", 2.99);
+ logger.HelloFrom("tomato", 2.99);
}
}
diff --git a/docs/trace/README.md b/docs/trace/README.md
index f0cdb973e1e..80f7c48978e 100644
--- a/docs/trace/README.md
+++ b/docs/trace/README.md
@@ -49,7 +49,7 @@ too frequently. `ActivitySource` is fairly expensive and meant to be reused
throughout the application. For most applications, it can be modeled as static
readonly field (e.g. [Program.cs](./getting-started-console/Program.cs)) or
singleton via dependency injection (e.g.
-[Instrumentation.cs](../../examples/AspNetCore/Instrumentation.cs)).
+[InstrumentationSource.cs](../../examples/AspNetCore/InstrumentationSource.cs)).
:heavy_check_mark: You should use dot-separated
[UpperCamelCase](https://en.wikipedia.org/wiki/Camel_case) as the
diff --git a/docs/trace/customizing-the-sdk/Program.cs b/docs/trace/customizing-the-sdk/Program.cs
index fead3aa0be2..1cc63b0fcc5 100644
--- a/docs/trace/customizing-the-sdk/Program.cs
+++ b/docs/trace/customizing-the-sdk/Program.cs
@@ -8,7 +8,7 @@
namespace CustomizingTheSdk;
-public class Program
+internal static class Program
{
private static readonly ActivitySource MyLibraryActivitySource = new(
"MyCompany.MyProduct.MyLibrary");
diff --git a/docs/trace/extending-the-sdk/MyEnrichingProcessor.cs b/docs/trace/extending-the-sdk/MyEnrichingProcessor.cs
index a6107330485..4372f9e59f3 100644
--- a/docs/trace/extending-the-sdk/MyEnrichingProcessor.cs
+++ b/docs/trace/extending-the-sdk/MyEnrichingProcessor.cs
@@ -4,7 +4,7 @@
using System.Diagnostics;
using OpenTelemetry;
-internal class MyEnrichingProcessor : BaseProcessor
+internal sealed class MyEnrichingProcessor : BaseProcessor
{
public override void OnEnd(Activity activity)
{
diff --git a/docs/trace/extending-the-sdk/MyExporter.cs b/docs/trace/extending-the-sdk/MyExporter.cs
index d37b9daac46..f00674d1ffc 100644
--- a/docs/trace/extending-the-sdk/MyExporter.cs
+++ b/docs/trace/extending-the-sdk/MyExporter.cs
@@ -5,7 +5,7 @@
using System.Text;
using OpenTelemetry;
-internal class MyExporter : BaseExporter
+internal sealed class MyExporter : BaseExporter
{
private readonly string name;
@@ -43,6 +43,7 @@ protected override bool OnShutdown(int timeoutMilliseconds)
protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
diff --git a/docs/trace/extending-the-sdk/MyProcessor.cs b/docs/trace/extending-the-sdk/MyProcessor.cs
index 4171821d49a..d6ab394ce03 100644
--- a/docs/trace/extending-the-sdk/MyProcessor.cs
+++ b/docs/trace/extending-the-sdk/MyProcessor.cs
@@ -4,7 +4,7 @@
using System.Diagnostics;
using OpenTelemetry;
-internal class MyProcessor : BaseProcessor
+internal sealed class MyProcessor : BaseProcessor
{
private readonly string name;
@@ -37,6 +37,7 @@ protected override bool OnShutdown(int timeoutMilliseconds)
protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
diff --git a/docs/trace/extending-the-sdk/MyResourceDetector.cs b/docs/trace/extending-the-sdk/MyResourceDetector.cs
index df40d9b3302..978e4a7edb6 100644
--- a/docs/trace/extending-the-sdk/MyResourceDetector.cs
+++ b/docs/trace/extending-the-sdk/MyResourceDetector.cs
@@ -3,7 +3,7 @@
using OpenTelemetry.Resources;
-internal class MyResourceDetector : IResourceDetector
+internal sealed class MyResourceDetector : IResourceDetector
{
public Resource Detect()
{
diff --git a/docs/trace/extending-the-sdk/MySampler.cs b/docs/trace/extending-the-sdk/MySampler.cs
index bbb4ae04793..2d733aed25d 100644
--- a/docs/trace/extending-the-sdk/MySampler.cs
+++ b/docs/trace/extending-the-sdk/MySampler.cs
@@ -3,7 +3,7 @@
using OpenTelemetry.Trace;
-internal class MySampler : Sampler
+internal sealed class MySampler : Sampler
{
public override SamplingResult ShouldSample(in SamplingParameters param)
{
diff --git a/docs/trace/extending-the-sdk/Program.cs b/docs/trace/extending-the-sdk/Program.cs
index ce001aee3e3..e051d4ed71a 100644
--- a/docs/trace/extending-the-sdk/Program.cs
+++ b/docs/trace/extending-the-sdk/Program.cs
@@ -8,7 +8,7 @@
namespace ExtendingTheSdk;
-public class Program
+internal static class Program
{
private static readonly ActivitySource DemoSource = new("OTel.Demo");
diff --git a/docs/trace/extending-the-sdk/extending-the-sdk.csproj b/docs/trace/extending-the-sdk/extending-the-sdk.csproj
index 85aab7a7ed3..2f56fd33163 100644
--- a/docs/trace/extending-the-sdk/extending-the-sdk.csproj
+++ b/docs/trace/extending-the-sdk/extending-the-sdk.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA2000;CA1812;CA1510
+
+
diff --git a/docs/trace/getting-started-console/Program.cs b/docs/trace/getting-started-console/Program.cs
index 4acaf7b8a80..3ab95d4d24f 100644
--- a/docs/trace/getting-started-console/Program.cs
+++ b/docs/trace/getting-started-console/Program.cs
@@ -5,7 +5,9 @@
using OpenTelemetry;
using OpenTelemetry.Trace;
-public class Program
+namespace GettingStartedConsole;
+
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("MyCompany.MyProduct.MyLibrary");
@@ -18,9 +20,10 @@ public static void Main()
using (var activity = MyActivitySource.StartActivity("SayHello"))
{
+ int[] intArray = [1, 2, 3];
activity?.SetTag("foo", 1);
activity?.SetTag("bar", "Hello, World!");
- activity?.SetTag("baz", new int[] { 1, 2, 3 });
+ activity?.SetTag("baz", intArray);
activity?.SetStatus(ActivityStatusCode.Ok);
}
diff --git a/docs/trace/getting-started-jaeger/Program.cs b/docs/trace/getting-started-jaeger/Program.cs
index d4603a71bd0..bff8079f132 100644
--- a/docs/trace/getting-started-jaeger/Program.cs
+++ b/docs/trace/getting-started-jaeger/Program.cs
@@ -11,7 +11,7 @@
namespace GettingStartedJaeger;
-public class Program
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("OpenTelemetry.Demo.Jaeger");
@@ -33,13 +33,13 @@ public static async Task Main()
{
using (var slow = MyActivitySource.StartActivity("SomethingSlow"))
{
- await client.GetStringAsync("https://httpstat.us/200?sleep=1000");
- await client.GetStringAsync("https://httpstat.us/200?sleep=1000");
+ await client.GetStringAsync(new Uri("https://httpstat.us/200?sleep=1000")).ConfigureAwait(false);
+ await client.GetStringAsync(new Uri("https://httpstat.us/200?sleep=1000")).ConfigureAwait(false);
}
using (var fast = MyActivitySource.StartActivity("SomethingFast"))
{
- await client.GetStringAsync("https://httpstat.us/301");
+ await client.GetStringAsync(new Uri("https://httpstat.us/301")).ConfigureAwait(false);
}
}
}
diff --git a/docs/trace/links-based-sampler/LinksAndParentBasedSampler.cs b/docs/trace/links-based-sampler/LinksAndParentBasedSampler.cs
index bca076a2734..af44cb92072 100644
--- a/docs/trace/links-based-sampler/LinksAndParentBasedSampler.cs
+++ b/docs/trace/links-based-sampler/LinksAndParentBasedSampler.cs
@@ -13,7 +13,7 @@ namespace LinksAndParentBasedSamplerExample;
/// links based sampler. If either of these samplers decide to sample,
/// this composite sampler decides to sample.
///
-internal class LinksAndParentBasedSampler : Sampler
+internal sealed class LinksAndParentBasedSampler : Sampler
{
private readonly ParentBasedSampler parentBasedSampler;
private readonly LinksBasedSampler linksBasedSampler;
diff --git a/docs/trace/links-based-sampler/LinksBasedSampler.cs b/docs/trace/links-based-sampler/LinksBasedSampler.cs
index b9306fab232..226b6591693 100644
--- a/docs/trace/links-based-sampler/LinksBasedSampler.cs
+++ b/docs/trace/links-based-sampler/LinksBasedSampler.cs
@@ -10,7 +10,7 @@ namespace LinksAndParentBasedSamplerExample;
/// A non-probabilistic sampler that samples an activity if ANY of the linked activities
/// is sampled.
///
-internal class LinksBasedSampler : Sampler
+internal sealed class LinksBasedSampler : Sampler
{
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
{
diff --git a/docs/trace/links-based-sampler/Program.cs b/docs/trace/links-based-sampler/Program.cs
index 7ef89aa5fdb..03827633820 100644
--- a/docs/trace/links-based-sampler/Program.cs
+++ b/docs/trace/links-based-sampler/Program.cs
@@ -7,7 +7,7 @@
namespace LinksAndParentBasedSamplerExample;
-internal class Program
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("LinksAndParentBasedSampler.Example");
@@ -37,7 +37,7 @@ public static void Main(string[] args)
/// Generates a list of activity links. A linked activity is sampled with a probability of 0.1.
///
/// A list of links.
- private static IEnumerable GetActivityLinks(int seed)
+ private static List GetActivityLinks(int seed)
{
var random = new Random(seed);
var linkedActivitiesList = new List();
diff --git a/docs/trace/links-based-sampler/links-sampler.csproj b/docs/trace/links-based-sampler/links-sampler.csproj
index 19aa9791432..97876c06b9c 100644
--- a/docs/trace/links-based-sampler/links-sampler.csproj
+++ b/docs/trace/links-based-sampler/links-sampler.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394
+
+
diff --git a/docs/trace/links-creation-with-new-activities/Program.cs b/docs/trace/links-creation-with-new-activities/Program.cs
index c03af7bcc82..1568930d998 100644
--- a/docs/trace/links-creation-with-new-activities/Program.cs
+++ b/docs/trace/links-creation-with-new-activities/Program.cs
@@ -7,7 +7,7 @@
namespace LinksCreationWithNewRootActivitiesDemo;
-internal class Program
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("LinksCreationWithNewRootActivities");
@@ -21,7 +21,7 @@ public static async Task Main(string[] args)
using (var activity = MyActivitySource.StartActivity("OrchestratingActivity"))
{
activity?.SetTag("foo", 1);
- await DoFanoutAsync();
+ await DoFanoutAsync().ConfigureAwait(false);
using (var nestedActivity = MyActivitySource.StartActivity("WrapUp"))
{
@@ -77,7 +77,7 @@ public static async Task DoFanoutAsync()
}
// Wait for all tasks to complete
- await Task.WhenAll(tasks);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
// Reset to the previous activity now that we are done with the fanout
// This will ensure that the rest of the code executes in the context of the original activity.
diff --git a/docs/trace/reporting-exceptions/Program.cs b/docs/trace/reporting-exceptions/Program.cs
index 62f2a11914e..a6e4997ff79 100644
--- a/docs/trace/reporting-exceptions/Program.cs
+++ b/docs/trace/reporting-exceptions/Program.cs
@@ -7,7 +7,7 @@
namespace ReportingExceptions;
-public class Program
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new(
"MyCompany.MyProduct.MyLibrary");
@@ -27,7 +27,7 @@ public static void Main()
{
using (MyActivitySource.StartActivity("Bar"))
{
- throw new Exception("Oops!");
+ throw new InvalidOperationException("Oops!");
}
}
}
diff --git a/docs/trace/stratified-sampling-example/Program.cs b/docs/trace/stratified-sampling-example/Program.cs
index 49d8e1b7e7b..8063dd4f046 100644
--- a/docs/trace/stratified-sampling-example/Program.cs
+++ b/docs/trace/stratified-sampling-example/Program.cs
@@ -7,7 +7,7 @@
namespace StratifiedSamplingByQueryTypeDemo;
-internal class Program
+internal sealed class Program
{
private static readonly ActivitySource MyActivitySource = new("StratifiedSampling.POC");
diff --git a/docs/trace/stratified-sampling-example/StratifiedSampler.cs b/docs/trace/stratified-sampling-example/StratifiedSampler.cs
index cb01a23dee0..64cbe8a4c84 100644
--- a/docs/trace/stratified-sampling-example/StratifiedSampler.cs
+++ b/docs/trace/stratified-sampling-example/StratifiedSampler.cs
@@ -5,7 +5,7 @@
namespace StratifiedSamplingByQueryTypeDemo;
-internal class StratifiedSampler : Sampler
+internal sealed class StratifiedSampler : Sampler
{
// For this POC, we have two groups.
// 0 is the group corresponding to user-initiated queries where we want a 100% sampling rate.
diff --git a/docs/trace/stratified-sampling-example/stratified-sampling-example.csproj b/docs/trace/stratified-sampling-example/stratified-sampling-example.csproj
index 19aa9791432..97876c06b9c 100644
--- a/docs/trace/stratified-sampling-example/stratified-sampling-example.csproj
+++ b/docs/trace/stratified-sampling-example/stratified-sampling-example.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394
+
+
diff --git a/docs/trace/tail-based-sampling-span-level/ParentBasedElseAlwaysRecordSampler.cs b/docs/trace/tail-based-sampling-span-level/ParentBasedElseAlwaysRecordSampler.cs
index bce691b0c16..4d933dbf2ec 100644
--- a/docs/trace/tail-based-sampling-span-level/ParentBasedElseAlwaysRecordSampler.cs
+++ b/docs/trace/tail-based-sampling-span-level/ParentBasedElseAlwaysRecordSampler.cs
@@ -15,7 +15,7 @@ namespace SDKBasedSpanLevelTailSamplingSample;
/// changed later by a span processor based on span attributes (e.g., failure) that become
/// available only by the end of the span.
///
-internal class ParentBasedElseAlwaysRecordSampler : Sampler
+internal sealed class ParentBasedElseAlwaysRecordSampler : Sampler
{
private const double DefaultSamplingProbabilityForRootSpan = 0.1;
private readonly ParentBasedSampler parentBasedSampler;
diff --git a/docs/trace/tail-based-sampling-span-level/Program.cs b/docs/trace/tail-based-sampling-span-level/Program.cs
index de4f8ea9f17..d2114248442 100644
--- a/docs/trace/tail-based-sampling-span-level/Program.cs
+++ b/docs/trace/tail-based-sampling-span-level/Program.cs
@@ -7,7 +7,7 @@
namespace SDKBasedSpanLevelTailSamplingSample;
-internal class Program
+internal static class Program
{
private static readonly ActivitySource MyActivitySource = new("SDK.TailSampling.POC");
diff --git a/docs/trace/tail-based-sampling-span-level/TailSamplingProcessor.cs b/docs/trace/tail-based-sampling-span-level/TailSamplingProcessor.cs
index 5aa22092e75..10aee54e38d 100644
--- a/docs/trace/tail-based-sampling-span-level/TailSamplingProcessor.cs
+++ b/docs/trace/tail-based-sampling-span-level/TailSamplingProcessor.cs
@@ -26,7 +26,7 @@ public override void OnEnd(Activity activity)
}
else
{
- this.IncludeForExportIfFailedActivity(activity);
+ IncludeForExportIfFailedActivity(activity);
}
base.OnEnd(activity);
@@ -42,7 +42,7 @@ public override void OnEnd(Activity activity)
// 2. Traces will not be complete: Since this sampling is at a span level, the generated trace will be partial and won't be complete.
// For example, if another part of the call tree is successful, those spans may not be sampled in leading to a partial trace.
// 3. If multiple exporters are used, this decision will impact all of them: https://github.com/open-telemetry/opentelemetry-dotnet/issues/3861.
- private void IncludeForExportIfFailedActivity(Activity activity)
+ private static void IncludeForExportIfFailedActivity(Activity activity)
{
if (activity.Status == ActivityStatusCode.Error)
{
diff --git a/docs/trace/tail-based-sampling-span-level/tail-based-sampling-example.csproj b/docs/trace/tail-based-sampling-span-level/tail-based-sampling-example.csproj
index 19aa9791432..b7947e79e17 100644
--- a/docs/trace/tail-based-sampling-span-level/tail-based-sampling-example.csproj
+++ b/docs/trace/tail-based-sampling-span-level/tail-based-sampling-example.csproj
@@ -1,4 +1,8 @@
+
+ $(NoWarn);CA5394;CA2000
+
+
diff --git a/examples/AspNetCore/Controllers/WeatherForecastController.cs b/examples/AspNetCore/Controllers/WeatherForecastController.cs
index 3d09fe9ed61..bbb257334e7 100644
--- a/examples/AspNetCore/Controllers/WeatherForecastController.cs
+++ b/examples/AspNetCore/Controllers/WeatherForecastController.cs
@@ -5,6 +5,7 @@ namespace Examples.AspNetCore.Controllers;
using System.Diagnostics;
using System.Diagnostics.Metrics;
+using System.Security.Cryptography;
using Examples.AspNetCore;
using Microsoft.AspNetCore.Mvc;
@@ -12,10 +13,10 @@ namespace Examples.AspNetCore.Controllers;
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
- private static readonly string[] Summaries = new[]
- {
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching",
- };
+ private static readonly string[] Summaries =
+ [
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ ];
private static readonly HttpClient HttpClient = new();
@@ -23,24 +24,24 @@ public class WeatherForecastController : ControllerBase
private readonly ActivitySource activitySource;
private readonly Counter freezingDaysCounter;
- public WeatherForecastController(ILogger logger, Instrumentation instrumentation)
+ public WeatherForecastController(ILogger logger, InstrumentationSource instrumentationSource)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- ArgumentNullException.ThrowIfNull(instrumentation);
- this.activitySource = instrumentation.ActivitySource;
- this.freezingDaysCounter = instrumentation.FreezingDaysCounter;
+ ArgumentNullException.ThrowIfNull(instrumentationSource);
+ this.activitySource = instrumentationSource.ActivitySource;
+ this.freezingDaysCounter = instrumentationSource.FreezingDaysCounter;
}
[HttpGet]
public IEnumerable Get()
{
- using var scope = this.logger.BeginScope("{Id}", Guid.NewGuid().ToString("N"));
+ using var scope = this.logger.BeginIdScope(Guid.NewGuid().ToString("N"));
- // Making an http call here to serve as an example of
+ // Making a http call here to serve as an example of
// how dependency calls will be captured and treated
// automatically as child of incoming request.
- var res = HttpClient.GetStringAsync("http://google.com").Result;
+ var res = HttpClient.GetStringAsync(new Uri("http://google.com")).Result;
// Optional: Manually create an activity. This will become a child of
// the activity created from the instrumentation library for AspNetCore.
@@ -52,22 +53,18 @@ public IEnumerable Get()
// a manual activity using Activity.Current?.SetTag()
using var activity = this.activitySource.StartActivity("calculate forecast");
- var rng = new Random();
var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
- TemperatureC = rng.Next(-20, 55),
- Summary = Summaries[rng.Next(Summaries.Length)],
+ TemperatureC = RandomNumberGenerator.GetInt32(-20, 55),
+ Summary = Summaries[RandomNumberGenerator.GetInt32(Summaries.Length)],
})
.ToArray();
// Optional: Count the freezing days
this.freezingDaysCounter.Add(forecast.Count(f => f.TemperatureC < 0));
- this.logger.LogInformation(
- "WeatherForecasts generated {count}: {forecasts}",
- forecast.Length,
- forecast);
+ this.logger.WeatherForecastGenerated(LogLevel.Information, forecast.Length, forecast);
return forecast;
}
diff --git a/examples/AspNetCore/Controllers/WeatherForecastControllerLog.cs b/examples/AspNetCore/Controllers/WeatherForecastControllerLog.cs
new file mode 100644
index 00000000000..eb6bc292673
--- /dev/null
+++ b/examples/AspNetCore/Controllers/WeatherForecastControllerLog.cs
@@ -0,0 +1,14 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+namespace Examples.AspNetCore.Controllers;
+
+internal static partial class WeatherForecastControllerLog
+{
+ private static readonly Func Scope = LoggerMessage.DefineScope("{Id}");
+
+ public static IDisposable? BeginIdScope(this ILogger logger, string id) => Scope(logger, id);
+
+ [LoggerMessage(EventId = 1, Message = "WeatherForecasts generated {Count}: {Forecasts}")]
+ public static partial void WeatherForecastGenerated(this ILogger logger, LogLevel logLevel, int count, WeatherForecast[] forecasts);
+}
diff --git a/examples/AspNetCore/Examples.AspNetCore.csproj b/examples/AspNetCore/Examples.AspNetCore.csproj
index e476b1ed4d8..459213fe2ba 100644
--- a/examples/AspNetCore/Examples.AspNetCore.csproj
+++ b/examples/AspNetCore/Examples.AspNetCore.csproj
@@ -2,6 +2,7 @@
$(DefaultTargetFrameworkForExampleApps)
+ $(NoWarn);CA1515
diff --git a/examples/AspNetCore/Instrumentation.cs b/examples/AspNetCore/InstrumentationSource.cs
similarity index 85%
rename from examples/AspNetCore/Instrumentation.cs
rename to examples/AspNetCore/InstrumentationSource.cs
index 4b0ede1157f..be6d5764033 100644
--- a/examples/AspNetCore/Instrumentation.cs
+++ b/examples/AspNetCore/InstrumentationSource.cs
@@ -11,15 +11,15 @@ namespace Examples.AspNetCore;
/// ActivitySource and Instruments. This avoids possible type collisions
/// with other components in the DI container.
///
-public class Instrumentation : IDisposable
+public sealed class InstrumentationSource : IDisposable
{
internal const string ActivitySourceName = "Examples.AspNetCore";
internal const string MeterName = "Examples.AspNetCore";
private readonly Meter meter;
- public Instrumentation()
+ public InstrumentationSource()
{
- string? version = typeof(Instrumentation).Assembly.GetName().Version?.ToString();
+ string? version = typeof(InstrumentationSource).Assembly.GetName().Version?.ToString();
this.ActivitySource = new ActivitySource(ActivitySourceName, version);
this.meter = new Meter(MeterName, version);
this.FreezingDaysCounter = this.meter.CreateCounter("weather.days.freezing", description: "The number of days where the temperature is below freezing");
diff --git a/examples/AspNetCore/Program.cs b/examples/AspNetCore/Program.cs
index 809534f5c76..afd59db31d7 100644
--- a/examples/AspNetCore/Program.cs
+++ b/examples/AspNetCore/Program.cs
@@ -13,20 +13,20 @@
var appBuilder = WebApplication.CreateBuilder(args);
// Note: Switch between Zipkin/OTLP/Console by setting UseTracingExporter in appsettings.json.
-var tracingExporter = appBuilder.Configuration.GetValue("UseTracingExporter", defaultValue: "console")!.ToLowerInvariant();
+var tracingExporter = appBuilder.Configuration.GetValue("UseTracingExporter", defaultValue: "CONSOLE").ToUpperInvariant();
// Note: Switch between Prometheus/OTLP/Console by setting UseMetricsExporter in appsettings.json.
-var metricsExporter = appBuilder.Configuration.GetValue("UseMetricsExporter", defaultValue: "console")!.ToLowerInvariant();
+var metricsExporter = appBuilder.Configuration.GetValue("UseMetricsExporter", defaultValue: "CONSOLE").ToUpperInvariant();
// Note: Switch between Console/OTLP by setting UseLogExporter in appsettings.json.
-var logExporter = appBuilder.Configuration.GetValue("UseLogExporter", defaultValue: "console")!.ToLowerInvariant();
+var logExporter = appBuilder.Configuration.GetValue("UseLogExporter", defaultValue: "CONSOLE").ToUpperInvariant();
// Note: Switch between Explicit/Exponential by setting HistogramAggregation in appsettings.json
-var histogramAggregation = appBuilder.Configuration.GetValue("HistogramAggregation", defaultValue: "explicit")!.ToLowerInvariant();
+var histogramAggregation = appBuilder.Configuration.GetValue("HistogramAggregation", defaultValue: "EXPLICIT").ToUpperInvariant();
// Create a service to expose ActivitySource, and Metric Instruments
// for manual instrumentation
-appBuilder.Services.AddSingleton();
+appBuilder.Services.AddSingleton();
// Clear default logging providers used by WebApplication host.
appBuilder.Logging.ClearProviders();
@@ -45,7 +45,7 @@
// Ensure the TracerProvider subscribes to any custom ActivitySources.
builder
- .AddSource(Instrumentation.ActivitySourceName)
+ .AddSource(InstrumentationSource.ActivitySourceName)
.SetSampler(new AlwaysOnSampler())
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation();
@@ -55,7 +55,7 @@
switch (tracingExporter)
{
- case "zipkin":
+ case "ZIPKIN":
builder.AddZipkinExporter();
builder.ConfigureServices(services =>
@@ -65,11 +65,11 @@
});
break;
- case "otlp":
+ case "OTLP":
builder.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
- otlpOptions.Endpoint = new Uri(appBuilder.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317")!);
+ otlpOptions.Endpoint = new Uri(appBuilder.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317"));
});
break;
@@ -84,7 +84,7 @@
// Ensure the MeterProvider subscribes to any custom Meters.
builder
- .AddMeter(Instrumentation.MeterName)
+ .AddMeter(InstrumentationSource.MeterName)
.SetExemplarFilter(ExemplarFilterType.TraceBased)
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation()
@@ -92,7 +92,7 @@
switch (histogramAggregation)
{
- case "exponential":
+ case "EXPONENTIAL":
builder.AddView(instrument =>
{
return instrument.GetType().GetGenericTypeDefinition() == typeof(Histogram<>)
@@ -108,10 +108,10 @@
switch (metricsExporter)
{
- case "prometheus":
+ case "PROMETHEUS":
builder.AddPrometheusExporter();
break;
- case "otlp":
+ case "OTLP":
builder.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
@@ -129,11 +129,11 @@
switch (logExporter)
{
- case "otlp":
+ case "OTLP":
builder.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
- otlpOptions.Endpoint = new Uri(appBuilder.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317")!);
+ otlpOptions.Endpoint = new Uri(appBuilder.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317"));
});
break;
default:
diff --git a/examples/AspNetCore/docker-compose.yaml b/examples/AspNetCore/docker-compose.yaml
index 7cdb95106e1..444e79eca81 100644
--- a/examples/AspNetCore/docker-compose.yaml
+++ b/examples/AspNetCore/docker-compose.yaml
@@ -1,9 +1,8 @@
-version: "3"
services:
# OTEL Collector to receive logs, metrics and traces from the application
otel-collector:
- image: otel/opentelemetry-collector:0.111.0
+ image: otel/opentelemetry-collector:0.136.0@sha256:98fd3b410ae8a939be9588f1580c4b7c3da6ebba49f5363df4259a827aabb779
command: [ "--config=/etc/otel-collector.yaml" ]
volumes:
- ./otel-collector.yaml:/etc/otel-collector.yaml
@@ -14,7 +13,7 @@ services:
# Exports Traces to Tempo
tempo:
- image: grafana/tempo:latest
+ image: grafana/tempo:2.8.2@sha256:0ef775495967cd5d7a6b2e146b6ea695d624803c8db8349fb8ce4164f719f9b7
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
@@ -26,7 +25,7 @@ services:
# Exports Metrics to Prometheus
prometheus:
- image: prom/prometheus:latest
+ image: prom/prometheus:v3.6.0@sha256:76947e7ef22f8a698fc638f706685909be425dbe09bd7a2cd7aca849f79b5f64
command:
- --config.file=/etc/prometheus.yaml
- --web.enable-remote-write-receiver
@@ -38,7 +37,7 @@ services:
# UI to query traces and metrics
grafana:
- image: grafana/grafana:9.3.2
+ image: grafana/grafana:12.2.0@sha256:74144189b38447facf737dfd0f3906e42e0776212bf575dc3334c3609183adf7
volumes:
- ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
environment:
diff --git a/examples/AspNetCore/tempo.yaml b/examples/AspNetCore/tempo.yaml
index 0d46d4718e8..1217bc05760 100644
--- a/examples/AspNetCore/tempo.yaml
+++ b/examples/AspNetCore/tempo.yaml
@@ -6,7 +6,9 @@ distributor:
otlp:
protocols:
http:
+ endpoint: 0.0.0.0:4318
grpc:
+ endpoint: 0.0.0.0:4317
storage:
trace:
diff --git a/examples/Console/Examples.Console.csproj b/examples/Console/Examples.Console.csproj
index 86c6b1df037..ade79a55665 100644
--- a/examples/Console/Examples.Console.csproj
+++ b/examples/Console/Examples.Console.csproj
@@ -1,9 +1,9 @@
-
+
Exe
$(DefaultTargetFrameworkForExampleApps)
- $(NoWarn),CS0618
+ $(NoWarn),CA1812
diff --git a/examples/Console/InstrumentationWithActivitySource.cs b/examples/Console/InstrumentationWithActivitySource.cs
index 706d68ad993..e591ea12505 100644
--- a/examples/Console/InstrumentationWithActivitySource.cs
+++ b/examples/Console/InstrumentationWithActivitySource.cs
@@ -8,7 +8,7 @@
namespace Examples.Console;
-internal class InstrumentationWithActivitySource : IDisposable
+internal sealed class InstrumentationWithActivitySource : IDisposable
{
private const string RequestPath = "/api/request";
private readonly SampleServer server = new();
@@ -27,7 +27,7 @@ public void Dispose()
this.server.Dispose();
}
- private class SampleServer : IDisposable
+ private sealed class SampleServer : IDisposable
{
private readonly HttpListener listener = new();
@@ -66,7 +66,7 @@ public void Start(string url)
}
activity?.SetTag("request.content", requestContent);
- activity?.SetTag("request.length", requestContent.Length.ToString());
+ activity?.SetTag("request.length", requestContent.Length);
var echo = Encoding.UTF8.GetBytes("echo: " + requestContent);
context.Response.ContentEncoding = Encoding.UTF8;
@@ -88,7 +88,7 @@ public void Dispose()
}
}
- private class SampleClient : IDisposable
+ private sealed class SampleClient : IDisposable
{
private CancellationTokenSource? cts;
private Task? requestTask;
@@ -114,7 +114,7 @@ public void Start(string url)
count++;
activity?.AddEvent(new ActivityEvent("PostAsync:Started"));
- using var response = await client.PostAsync(url, content, cancellationToken).ConfigureAwait(false);
+ using var response = await client.PostAsync(new Uri(url, UriKind.Absolute), content, cancellationToken).ConfigureAwait(false);
activity?.AddEvent(new ActivityEvent("PostAsync:Ended"));
activity?.SetTag("http.status_code", (int)response.StatusCode);
diff --git a/examples/Console/Program.cs b/examples/Console/Program.cs
index bca2e4339c7..a7d0e822f47 100644
--- a/examples/Console/Program.cs
+++ b/examples/Console/Program.cs
@@ -8,7 +8,7 @@ namespace Examples.Console;
///
/// Main samples entry point.
///
-public class Program
+internal static class Program
{
///
/// Main method - invoke this using command line.
@@ -51,21 +51,21 @@ public static void Main(string[] args)
#pragma warning disable SA1402 // File may only contain a single type
[Verb("zipkin", HelpText = "Specify the options required to test Zipkin exporter")]
-internal class ZipkinOptions
+internal sealed class ZipkinOptions
{
[Option('u', "uri", HelpText = "Please specify the uri of Zipkin backend", Required = true)]
public required string Uri { get; set; }
}
[Verb("prometheus", HelpText = "Specify the options required to test Prometheus")]
-internal class PrometheusOptions
+internal sealed class PrometheusOptions
{
[Option('p', "port", Default = 9464, HelpText = "The port to expose metrics. The endpoint will be http://localhost:port/metrics/ (this is the port from which your Prometheus server scraps metrics from.)", Required = false)]
public int Port { get; set; }
}
[Verb("metrics", HelpText = "Specify the options required to test Metrics")]
-internal class MetricsOptions
+internal sealed class MetricsOptions
{
[Option('d', "IsDelta", HelpText = "Export Delta metrics", Required = false, Default = false)]
public bool IsDelta { get; set; }
@@ -93,32 +93,32 @@ internal class MetricsOptions
}
[Verb("grpc", HelpText = "Specify the options required to test Grpc.Net.Client")]
-internal class GrpcNetClientOptions
+internal sealed class GrpcNetClientOptions
{
}
[Verb("httpclient", HelpText = "Specify the options required to test HttpClient")]
-internal class HttpClientOptions
+internal sealed class HttpClientOptions
{
}
[Verb("console", HelpText = "Specify the options required to test console exporter")]
-internal class ConsoleOptions
+internal sealed class ConsoleOptions
{
}
[Verb("otelshim", HelpText = "Specify the options required to test OpenTelemetry Shim with console exporter")]
-internal class OpenTelemetryShimOptions
+internal sealed class OpenTelemetryShimOptions
{
}
[Verb("opentracing", HelpText = "Specify the options required to test OpenTracing Shim with console exporter")]
-internal class OpenTracingShimOptions
+internal sealed class OpenTracingShimOptions
{
}
[Verb("otlp", HelpText = "Specify the options required to test OpenTelemetry Protocol (OTLP)")]
-internal class OtlpOptions
+internal sealed class OtlpOptions
{
[Option('e', "endpoint", HelpText = "Target to which the exporter is going to send traces (default value depends on protocol).", Default = null)]
public string? Endpoint { get; set; }
@@ -128,7 +128,7 @@ internal class OtlpOptions
}
[Verb("logs", HelpText = "Specify the options required to test Logs")]
-internal class LogsOptions
+internal sealed class LogsOptions
{
[Option("useExporter", Default = "otlp", HelpText = "Options include otlp or console.", Required = false)]
public string? UseExporter { get; set; }
@@ -147,7 +147,7 @@ internal class LogsOptions
}
[Verb("inmemory", HelpText = "Specify the options required to test InMemory Exporter")]
-internal class InMemoryOptions
+internal sealed class InMemoryOptions
{
}
diff --git a/examples/Console/TestConsoleExporter.cs b/examples/Console/TestConsoleExporter.cs
index 7a70c0ef1e4..fa59c3a9954 100644
--- a/examples/Console/TestConsoleExporter.cs
+++ b/examples/Console/TestConsoleExporter.cs
@@ -8,7 +8,7 @@
namespace Examples.Console;
-internal class TestConsoleExporter
+internal sealed class TestConsoleExporter
{
// To run this example, run the following command from
// the reporoot\examples\Console\.
@@ -27,21 +27,21 @@ private static int RunWithActivitySource()
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource("Samples.SampleClient", "Samples.SampleServer")
.ConfigureResource(res => res.AddService("console-test"))
+#pragma warning disable CA2000 // Dispose objects before losing scope
.AddProcessor(new MyProcessor()) // This must be added before ConsoleExporter
+#pragma warning restore CA2000 // Dispose objects before losing scope
.AddConsoleExporter()
.Build();
// The above line is required only in applications
// which decide to use OpenTelemetry.
- using (var sample = new InstrumentationWithActivitySource())
- {
- sample.Start();
+ using var sample = new InstrumentationWithActivitySource();
+ sample.Start();
- System.Console.WriteLine("Traces are being created and exported " +
- "to Console in the background. " +
- "Press ENTER to stop.");
- System.Console.ReadLine();
- }
+ System.Console.WriteLine("Traces are being created and exported " +
+ "to Console in the background. " +
+ "Press ENTER to stop.");
+ System.Console.ReadLine();
return 0;
}
@@ -50,7 +50,7 @@ private static int RunWithActivitySource()
/// An example of custom processor which
/// can be used to add more tags to an activity.
///
- internal class MyProcessor : BaseProcessor
+ internal sealed class MyProcessor : BaseProcessor
{
public override void OnStart(Activity activity)
{
diff --git a/examples/Console/TestGrpcNetClient.cs b/examples/Console/TestGrpcNetClient.cs
index 31dce4a1679..ab2e0378a6b 100644
--- a/examples/Console/TestGrpcNetClient.cs
+++ b/examples/Console/TestGrpcNetClient.cs
@@ -10,7 +10,7 @@
namespace Examples.Console;
-internal class TestGrpcNetClient
+internal sealed class TestGrpcNetClient
{
internal static int Run(GrpcNetClientOptions options)
{
diff --git a/examples/Console/TestHttpClient.cs b/examples/Console/TestHttpClient.cs
index 7629a622af7..5c72029378c 100644
--- a/examples/Console/TestHttpClient.cs
+++ b/examples/Console/TestHttpClient.cs
@@ -8,7 +8,7 @@
namespace Examples.Console;
-internal class TestHttpClient
+internal sealed class TestHttpClient
{
// To run this example, run the following command from
// the reporoot\examples\Console\.
@@ -32,7 +32,7 @@ internal static int Run(HttpClientOptions options)
using (var parent = source.StartActivity("incoming request", ActivityKind.Server))
{
using var client = new HttpClient();
- client.GetStringAsync("http://bing.com").GetAwaiter().GetResult();
+ client.GetStringAsync(new Uri("http://bing.com", UriKind.Absolute)).GetAwaiter().GetResult();
}
System.Console.WriteLine("Press Enter key to exit.");
diff --git a/examples/Console/TestInMemoryExporter.cs b/examples/Console/TestInMemoryExporter.cs
index 057bc19cdec..d3b30db60ff 100644
--- a/examples/Console/TestInMemoryExporter.cs
+++ b/examples/Console/TestInMemoryExporter.cs
@@ -8,7 +8,7 @@
namespace Examples.Console;
-internal class TestInMemoryExporter
+internal sealed class TestInMemoryExporter
{
// To run this example, run the following command from
// the reporoot\examples\Console\.
diff --git a/examples/Console/TestLogs.cs b/examples/Console/TestLogs.cs
index 427193799ac..b38cc230217 100644
--- a/examples/Console/TestLogs.cs
+++ b/examples/Console/TestLogs.cs
@@ -7,7 +7,7 @@
namespace Examples.Console;
-internal class TestLogs
+internal sealed class TestLogs
{
internal static int Run(LogsOptions options)
{
@@ -28,10 +28,10 @@ internal static int Run(LogsOptions options)
* launch the OpenTelemetry Collector with an OTLP receiver, by running:
*
* - On Unix based systems use:
- * docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:0.48.0 --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* - On Windows use:
- * docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:0.48.0 --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* Open another terminal window at the examples/Console/ directory and
* launch the OTLP example by running:
@@ -112,11 +112,11 @@ internal static int Run(LogsOptions options)
});
});
- var logger = loggerFactory.CreateLogger();
- using (logger.BeginScope("{city}", "Seattle"))
- using (logger.BeginScope("{storeType}", "Physical"))
+ var logger = loggerFactory.CreateLogger();
+ using (logger.BeginCityScope("Seattle"))
+ using (logger.BeginStoreTypeScope("Physical"))
{
- logger.LogInformation("Hello from {name} {price}.", "tomato", 2.99);
+ logger.HelloFrom("tomato", 2.99);
}
return 0;
diff --git a/examples/Console/TestLogsExtensions.cs b/examples/Console/TestLogsExtensions.cs
new file mode 100644
index 00000000000..f76eb03b3c8
--- /dev/null
+++ b/examples/Console/TestLogsExtensions.cs
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace Examples.Console;
+
+internal static partial class TestLogsExtensions
+{
+ private static readonly Func CityScope = LoggerMessage.DefineScope("{City}");
+ private static readonly Func StoreTypeScope = LoggerMessage.DefineScope("{StoreType}");
+
+ public static IDisposable? BeginCityScope(this ILogger logger, string city) => CityScope(logger, city);
+
+ public static IDisposable? BeginStoreTypeScope(this ILogger logger, string storeType) => StoreTypeScope(logger, storeType);
+
+ [LoggerMessage(LogLevel.Information, "Hello from {Name} {Price}.")]
+ public static partial void HelloFrom(this ILogger logger, string name, double price);
+}
diff --git a/examples/Console/TestMetrics.cs b/examples/Console/TestMetrics.cs
index 9582a0f561e..308acee8d4c 100644
--- a/examples/Console/TestMetrics.cs
+++ b/examples/Console/TestMetrics.cs
@@ -10,7 +10,7 @@
namespace Examples.Console;
-internal class TestMetrics
+internal sealed class TestMetrics
{
internal static int Run(MetricsOptions options)
{
@@ -37,10 +37,10 @@ internal static int Run(MetricsOptions options)
* launch the OpenTelemetry Collector with an OTLP receiver, by running:
*
* - On Unix based systems use:
- * docker run --rm -it -p 4317:4317 -v $(pwd):/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -v $(pwd):/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* - On Windows use:
- * docker run --rm -it -p 4317:4317 -v "%cd%":/cfg otel/opentelemetry-collector:0.33.0 --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -v "%cd%":/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* Open another terminal window at the examples/Console/ directory and
* launch the OTLP example by running:
@@ -97,7 +97,7 @@ internal static int Run(MetricsOptions options)
{
return new List>()
{
- new Measurement(
+ new(
(int)Process.GetCurrentProcess().PrivateMemorySize64,
new KeyValuePair("tag1", "value1")),
};
diff --git a/examples/Console/TestOTelShimWithConsoleExporter.cs b/examples/Console/TestOTelShimWithConsoleExporter.cs
index 42e6f368f54..4dbb22ff9db 100644
--- a/examples/Console/TestOTelShimWithConsoleExporter.cs
+++ b/examples/Console/TestOTelShimWithConsoleExporter.cs
@@ -7,7 +7,7 @@
namespace Examples.Console;
-internal class TestOTelShimWithConsoleExporter
+internal sealed class TestOTelShimWithConsoleExporter
{
internal static int Run(OpenTelemetryShimOptions options)
{
diff --git a/examples/Console/TestOpenTracingShim.cs b/examples/Console/TestOpenTracingShim.cs
index 386cfe48ab1..77ab8d6c7d0 100644
--- a/examples/Console/TestOpenTracingShim.cs
+++ b/examples/Console/TestOpenTracingShim.cs
@@ -10,7 +10,7 @@
namespace Examples.Console;
-internal class TestOpenTracingShim
+internal sealed class TestOpenTracingShim
{
internal static int Run(OpenTracingShimOptions options)
{
diff --git a/examples/Console/TestOtlpExporter.cs b/examples/Console/TestOtlpExporter.cs
index 3af0ceea14b..c38ac518a18 100644
--- a/examples/Console/TestOtlpExporter.cs
+++ b/examples/Console/TestOtlpExporter.cs
@@ -20,10 +20,10 @@ internal static int Run(OtlpOptions options)
* launch the OpenTelemetry Collector with an OTLP receiver, by running:
*
* - On Unix based systems use:
- * docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* - On Windows use:
- * docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otlp-collector-example/config.yaml
+ * docker run --rm -it -p 4317:4317 -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:0.123.0 --config=/cfg/otlp-collector-example/config.yaml
*
* Open another terminal window at the examples/Console/ directory and
* launch the OTLP example by running:
@@ -69,24 +69,22 @@ private static int RunWithActivitySource(OtlpOptions options)
// The above line is required only in Applications
// which decide to use OpenTelemetry.
- using (var sample = new InstrumentationWithActivitySource())
- {
- sample.Start();
+ using var sample = new InstrumentationWithActivitySource();
+ sample.Start();
- System.Console.WriteLine("Traces are being created and exported " +
- "to the OpenTelemetry Collector in the background. " +
- "Press ENTER to stop.");
- System.Console.ReadLine();
- }
+ System.Console.WriteLine("Traces are being created and exported " +
+ "to the OpenTelemetry Collector in the background. " +
+ "Press ENTER to stop.");
+ System.Console.ReadLine();
return 0;
}
private static OtlpExportProtocol? ToOtlpExportProtocol(string? protocol) =>
- protocol?.Trim().ToLower() switch
+ protocol?.Trim().ToUpperInvariant() switch
{
- "grpc" => OtlpExportProtocol.Grpc,
- "http/protobuf" => OtlpExportProtocol.HttpProtobuf,
+ "GRPC" => OtlpExportProtocol.Grpc,
+ "HTTP/PROTOBUF" => OtlpExportProtocol.HttpProtobuf,
_ => null,
};
}
diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs
index a6bc1888f2b..3dc9ca0d89d 100644
--- a/examples/Console/TestPrometheusExporter.cs
+++ b/examples/Console/TestPrometheusExporter.cs
@@ -3,19 +3,18 @@
using System.Diagnostics;
using System.Diagnostics.Metrics;
+using System.Security.Cryptography;
using OpenTelemetry;
using OpenTelemetry.Metrics;
-using OpenTelemetry.Trace;
namespace Examples.Console;
-internal class TestPrometheusExporter
+internal sealed class TestPrometheusExporter
{
private static readonly Meter MyMeter = new("MyMeter");
private static readonly Meter MyMeter2 = new("MyMeter2");
private static readonly Counter Counter = MyMeter.CreateCounter("myCounter", description: "A counter for demonstration purpose.");
private static readonly Histogram MyHistogram = MyMeter.CreateHistogram("myHistogram");
- private static readonly ThreadLocal ThreadLocalRandom = new(() => new Random());
internal static int Run(PrometheusOptions options)
{
@@ -35,7 +34,7 @@ internal static int Run(PrometheusOptions options)
.AddMeter(MyMeter.Name)
.AddMeter(MyMeter2.Name)
.AddPrometheusHttpListener(
- o => o.UriPrefixes = new string[] { $"http://localhost:{options.Port}/" })
+ o => o.UriPrefixes = [$"http://localhost:{options.Port}/"])
.Build();
var process = Process.GetCurrentProcess();
@@ -57,7 +56,7 @@ internal static int Run(PrometheusOptions options)
{
Counter.Add(9.9, new("name", "apple"), new("color", "red"));
Counter.Add(99.9, new("name", "lemon"), new("color", "yellow"));
- MyHistogram.Record(ThreadLocalRandom.Value!.Next(1, 1500), new("tag1", "value1"), new("tag2", "value2"));
+ MyHistogram.Record(RandomNumberGenerator.GetInt32(1, 1500), new("tag1", "value1"), new("tag2", "value2"));
Task.Delay(10).Wait();
}
});
diff --git a/examples/Console/TestZipkinExporter.cs b/examples/Console/TestZipkinExporter.cs
index 0bb64323442..d7d579270f9 100644
--- a/examples/Console/TestZipkinExporter.cs
+++ b/examples/Console/TestZipkinExporter.cs
@@ -7,7 +7,7 @@
namespace Examples.Console;
-internal class TestZipkinExporter
+internal sealed class TestZipkinExporter
{
internal static int Run(ZipkinOptions options)
{
@@ -33,15 +33,13 @@ internal static int Run(ZipkinOptions options)
})
.Build();
- using (var sample = new InstrumentationWithActivitySource())
- {
- sample.Start();
+ using var sample = new InstrumentationWithActivitySource();
+ sample.Start();
- System.Console.WriteLine("Traces are being created and exported " +
- "to Zipkin in the background. Use Zipkin to view them. " +
- "Press ENTER to stop.");
- System.Console.ReadLine();
- }
+ System.Console.WriteLine("Traces are being created and exported " +
+ "to Zipkin in the background. Use Zipkin to view them. " +
+ "Press ENTER to stop.");
+ System.Console.ReadLine();
return 0;
}
diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props
index 032e1e27ec9..4c709d51681 100644
--- a/examples/Directory.Build.props
+++ b/examples/Directory.Build.props
@@ -1,3 +1,4 @@
+
diff --git a/examples/GrpcService/Program.cs b/examples/GrpcService/Program.cs
index 5ed8ceb4c34..5f80792c640 100644
--- a/examples/GrpcService/Program.cs
+++ b/examples/GrpcService/Program.cs
@@ -1,22 +1,58 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-namespace Examples.GrpcService;
+using Examples.GrpcService;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
-public class Program
-{
- public static void Main(string[] args)
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.AddGrpc();
+
+builder.Services.AddOpenTelemetry()
+ .WithTracing(tracerBuilder =>
{
- CreateHostBuilder(args).Build().Run();
- }
-
- // Additional configuration is required to successfully run gRPC on macOS.
- // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup();
- webBuilder.UseUrls("https://localhost:44335");
- });
+ tracerBuilder.ConfigureResource(r => r.AddService(
+ builder.Configuration.GetValue("ServiceName", defaultValue: "otel-test")))
+ .AddAspNetCoreInstrumentation();
+
+ var exporter = builder.Configuration.GetValue("UseExporter", defaultValue: "console").ToUpperInvariant();
+ switch (exporter)
+ {
+ case "OTLP":
+ tracerBuilder.AddOtlpExporter(otlpOptions =>
+ {
+ otlpOptions.Endpoint = new Uri(builder.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317"));
+ });
+ break;
+ case "ZIPKIN":
+ tracerBuilder.AddZipkinExporter(zipkinOptions =>
+ {
+ zipkinOptions.Endpoint = new Uri(builder.Configuration.GetValue("Zipkin:Endpoint", defaultValue: "http://localhost:9411/api/v2/spans"));
+ });
+ break;
+ default:
+ tracerBuilder.AddConsoleExporter();
+ break;
+ }
+ });
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseDeveloperExceptionPage();
}
+
+app.UseRouting();
+
+app.MapGrpcService();
+app.MapGet("/", () =>
+{
+ return Results.Text("Communication with gRPC endpoints must be made through a gRPC client. " +
+ "To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
+});
+
+app.Run();
diff --git a/examples/GrpcService/Services/GreeterService.cs b/examples/GrpcService/Services/GreeterService.cs
index 011439970fb..9963b7aeb35 100644
--- a/examples/GrpcService/Services/GreeterService.cs
+++ b/examples/GrpcService/Services/GreeterService.cs
@@ -5,15 +5,10 @@
namespace Examples.GrpcService;
-public class GreeterService : Greeter.GreeterBase
+#pragma warning disable CA1812 // Avoid uninstantiated internal classes
+internal sealed class GreeterService : Greeter.GreeterBase
+#pragma warning restore CA1812 // Avoid uninstantiated internal classes
{
- private readonly ILogger logger;
-
- public GreeterService(ILogger logger)
- {
- this.logger = logger;
- }
-
public override Task SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
diff --git a/examples/GrpcService/Startup.cs b/examples/GrpcService/Startup.cs
deleted file mode 100644
index d23af73550f..00000000000
--- a/examples/GrpcService/Startup.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using OpenTelemetry.Resources;
-using OpenTelemetry.Trace;
-
-namespace Examples.GrpcService;
-
-public class Startup
-{
- public Startup(IConfiguration configuration)
- {
- this.Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddGrpc();
-
- services.AddOpenTelemetry()
- .WithTracing(builder =>
- {
- builder
- .ConfigureResource(r => r.AddService(this.Configuration.GetValue("ServiceName", defaultValue: "otel-test")!))
- .AddAspNetCoreInstrumentation();
-
- // Switch between Otlp/Zipkin/Console by setting UseExporter in appsettings.json.
- var exporter = this.Configuration.GetValue("UseExporter", defaultValue: "console")!.ToLowerInvariant();
- switch (exporter)
- {
- case "otlp":
- builder.AddOtlpExporter(otlpOptions =>
- {
- otlpOptions.Endpoint = new Uri(this.Configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317")!);
- });
- break;
- case "zipkin":
- builder.AddZipkinExporter(zipkinOptions =>
- {
- zipkinOptions.Endpoint = new Uri(this.Configuration.GetValue("Zipkin:Endpoint", defaultValue: "http://localhost:9411/api/v2/spans")!);
- });
- break;
- default:
- builder.AddConsoleExporter();
- break;
- }
- });
- }
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseRouting();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapGrpcService();
-
- endpoints.MapGet("/", async context =>
- {
- await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909").ConfigureAwait(false);
- });
- });
- }
-}
diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs b/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs
index 09de208df21..e3137d987a4 100644
--- a/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs
+++ b/examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
@@ -11,35 +12,39 @@
namespace Utils.Messaging;
-public class MessageReceiver : IDisposable
+public sealed class MessageReceiver : IDisposable
{
private static readonly ActivitySource ActivitySource = new(nameof(MessageReceiver));
private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator;
private readonly ILogger logger;
- private readonly IConnection connection;
- private readonly IModel channel;
+ private IConnection? connection;
+ private IChannel? channel;
public MessageReceiver(ILogger logger)
{
this.logger = logger;
- this.connection = RabbitMqHelper.CreateConnection();
- this.channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(this.connection);
}
public void Dispose()
{
- this.channel.Dispose();
- this.connection.Dispose();
+ this.channel?.Dispose();
+ this.connection?.Dispose();
}
- public void StartConsumer()
+ public async Task StartConsumerAsync()
{
- RabbitMqHelper.StartConsumer(this.channel, this.ReceiveMessage);
+ this.connection = await RabbitMqHelper.CreateConnectionAsync().ConfigureAwait(false);
+ this.channel = await RabbitMqHelper.CreateModelAndDeclareTestQueueAsync(this.connection).ConfigureAwait(false);
+ await RabbitMqHelper.StartConsumerAsync(this.channel, this.ReceiveMessageAsync).ConfigureAwait(false);
}
- public void ReceiveMessage(BasicDeliverEventArgs ea)
+ public async Task ReceiveMessageAsync(BasicDeliverEventArgs ea)
{
+ this.EnsureStarted();
+
+ ArgumentNullException.ThrowIfNull(ea);
+
// Extract the PropagationContext of the upstream parent from the message headers.
var parentContext = Propagator.Extract(default, ea.BasicProperties, this.ExtractTraceContextFromBasicProperties);
Baggage.Current = parentContext.Baggage;
@@ -53,7 +58,7 @@ public void ReceiveMessage(BasicDeliverEventArgs ea)
{
var message = Encoding.UTF8.GetString(ea.Body.Span.ToArray());
- this.logger.LogInformation($"Message received: [{message}]");
+ this.logger.MessageReceived(message);
activity?.SetTag("message", message);
@@ -61,29 +66,38 @@ public void ReceiveMessage(BasicDeliverEventArgs ea)
RabbitMqHelper.AddMessagingTags(activity);
// Simulate some work
- Thread.Sleep(1000);
+ await Task.Delay(1_000).ConfigureAwait(false);
}
catch (Exception ex)
{
- this.logger.LogError(ex, "Message processing failed.");
+ this.logger.MessageProcessingFailed(ex);
}
}
- private IEnumerable ExtractTraceContextFromBasicProperties(IBasicProperties props, string key)
+ private IEnumerable ExtractTraceContextFromBasicProperties(IReadOnlyBasicProperties props, string key)
{
try
{
- if (props.Headers.TryGetValue(key, out var value))
+ if (props.Headers?.TryGetValue(key, out var value) is true)
{
- var bytes = value as byte[];
- return new[] { Encoding.UTF8.GetString(bytes) };
+ var bytes = (byte[])value!;
+ return [Encoding.UTF8.GetString(bytes)];
}
}
catch (Exception ex)
{
- this.logger.LogError(ex, "Failed to extract trace context.");
+ this.logger.FailedToExtractTraceContext(ex);
}
- return Enumerable.Empty();
+ return [];
+ }
+
+ [MemberNotNull(nameof(channel), nameof(connection))]
+ private void EnsureStarted()
+ {
+ if (this.channel == null || this.connection == null)
+ {
+ throw new InvalidOperationException("The message receiver has not been started.");
+ }
}
}
diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageReceiverLog.cs b/examples/MicroserviceExample/Utils/Messaging/MessageReceiverLog.cs
new file mode 100644
index 00000000000..95a3e69a93a
--- /dev/null
+++ b/examples/MicroserviceExample/Utils/Messaging/MessageReceiverLog.cs
@@ -0,0 +1,18 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace Utils.Messaging;
+
+internal static partial class MessageReceiverLog
+{
+ [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Message received: [{Message}]")]
+ public static partial void MessageReceived(this ILogger logger, string message);
+
+ [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "Message processing failed.")]
+ public static partial void MessageProcessingFailed(this ILogger logger, Exception exception);
+
+ [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "Failed to extract trace context.")]
+ public static partial void FailedToExtractTraceContext(this ILogger logger, Exception exception);
+}
diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs
index 969cf279cb4..e978cf01f3e 100644
--- a/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs
+++ b/examples/MicroserviceExample/Utils/Messaging/MessageSender.cs
@@ -10,38 +10,42 @@
namespace Utils.Messaging;
-public class MessageSender : IDisposable
+public sealed class MessageSender : IDisposable
{
private static readonly ActivitySource ActivitySource = new(nameof(MessageSender));
private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator;
private readonly ILogger logger;
- private readonly IConnection connection;
- private readonly IModel channel;
+ private IConnection? connection;
+ private IChannel? channel;
public MessageSender(ILogger logger)
{
this.logger = logger;
- this.connection = RabbitMqHelper.CreateConnection();
- this.channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(this.connection);
}
public void Dispose()
{
- this.channel.Dispose();
- this.connection.Dispose();
+ this.channel?.Dispose();
+ this.connection?.Dispose();
}
- public string SendMessage()
+ public async Task SendMessageAsync()
{
try
{
+ if (this.channel is null)
+ {
+ this.connection = await RabbitMqHelper.CreateConnectionAsync().ConfigureAwait(false);
+ this.channel = await RabbitMqHelper.CreateModelAndDeclareTestQueueAsync(this.connection).ConfigureAwait(false);
+ }
+
// Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification.
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#span-name
var activityName = $"{RabbitMqHelper.TestQueueName} send";
using var activity = ActivitySource.StartActivity(activityName, ActivityKind.Producer);
- var props = this.channel.CreateBasicProperties();
+ var props = new BasicProperties();
// Depending on Sampling (and whether a listener is registered or not), the
// activity above may not be created.
@@ -65,19 +69,20 @@ public string SendMessage()
RabbitMqHelper.AddMessagingTags(activity);
var body = $"Published message: DateTime.Now = {DateTime.Now}.";
- this.channel.BasicPublish(
+ await this.channel.BasicPublishAsync(
exchange: RabbitMqHelper.DefaultExchangeName,
routingKey: RabbitMqHelper.TestQueueName,
+ mandatory: false,
basicProperties: props,
- body: Encoding.UTF8.GetBytes(body));
+ body: Encoding.UTF8.GetBytes(body)).ConfigureAwait(false);
- this.logger.LogInformation($"Message sent: [{body}]");
+ this.logger.MessageSent(body);
return body;
}
catch (Exception ex)
{
- this.logger.LogError(ex, "Message publishing failed.");
+ this.logger.MessagePublishingFailed(ex);
throw;
}
}
@@ -86,16 +91,13 @@ private void InjectTraceContextIntoBasicProperties(IBasicProperties props, strin
{
try
{
- if (props.Headers == null)
- {
- props.Headers = new Dictionary();
- }
+ props.Headers ??= new Dictionary();
props.Headers[key] = value;
}
catch (Exception ex)
{
- this.logger.LogError(ex, "Failed to inject trace context.");
+ this.logger.FailedToInjectTraceContext(ex);
}
}
}
diff --git a/examples/MicroserviceExample/Utils/Messaging/MessageSenderLog.cs b/examples/MicroserviceExample/Utils/Messaging/MessageSenderLog.cs
new file mode 100644
index 00000000000..f33eab31c37
--- /dev/null
+++ b/examples/MicroserviceExample/Utils/Messaging/MessageSenderLog.cs
@@ -0,0 +1,18 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace Utils.Messaging;
+
+internal static partial class MessageSenderLog
+{
+ [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Message sent: [{Body}]")]
+ public static partial void MessageSent(this ILogger logger, string body);
+
+ [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "Message publishing failed.")]
+ public static partial void MessagePublishingFailed(this ILogger logger, Exception exception);
+
+ [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "Failed to inject trace context.")]
+ public static partial void FailedToInjectTraceContext(this ILogger logger, Exception exception);
+}
diff --git a/examples/MicroserviceExample/Utils/Messaging/RabbitMqHelper.cs b/examples/MicroserviceExample/Utils/Messaging/RabbitMqHelper.cs
index cbc8f0d8344..a1a6569abbe 100644
--- a/examples/MicroserviceExample/Utils/Messaging/RabbitMqHelper.cs
+++ b/examples/MicroserviceExample/Utils/Messaging/RabbitMqHelper.cs
@@ -12,46 +12,41 @@ public static class RabbitMqHelper
public const string DefaultExchangeName = "";
public const string TestQueueName = "TestQueue";
- private static readonly ConnectionFactory ConnectionFactory;
-
- static RabbitMqHelper()
+ private static readonly ConnectionFactory ConnectionFactory = new()
{
- ConnectionFactory = new ConnectionFactory()
- {
- HostName = Environment.GetEnvironmentVariable("RABBITMQ_HOSTNAME") ?? "localhost",
- UserName = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_USER") ?? "guest",
- Password = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_PASS") ?? "guest",
- Port = 5672,
- RequestedConnectionTimeout = TimeSpan.FromMilliseconds(3000),
- };
- }
+ HostName = Environment.GetEnvironmentVariable("RABBITMQ_HOSTNAME") ?? "localhost",
+ UserName = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_USER") ?? "guest",
+ Password = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_PASS") ?? "guest",
+ Port = 5672,
+ RequestedConnectionTimeout = TimeSpan.FromMilliseconds(3000),
+ };
- public static IConnection CreateConnection()
- {
- return ConnectionFactory.CreateConnection();
- }
+ public static async Task CreateConnectionAsync() =>
+ await ConnectionFactory.CreateConnectionAsync().ConfigureAwait(false);
- public static IModel CreateModelAndDeclareTestQueue(IConnection connection)
+ public static async Task CreateModelAndDeclareTestQueueAsync(IConnection connection)
{
- var channel = connection.CreateModel();
+ ArgumentNullException.ThrowIfNull(connection);
+
+ var channel = await connection.CreateChannelAsync().ConfigureAwait(false);
- channel.QueueDeclare(
+ await channel.QueueDeclareAsync(
queue: TestQueueName,
durable: false,
exclusive: false,
autoDelete: false,
- arguments: null);
+ arguments: null).ConfigureAwait(false);
return channel;
}
- public static void StartConsumer(IModel channel, Action processMessage)
+ public static async Task StartConsumerAsync(IChannel channel, Func processMessage)
{
- var consumer = new EventingBasicConsumer(channel);
+ var consumer = new AsyncEventingBasicConsumer(channel);
- consumer.Received += (bc, ea) => processMessage(ea);
+ consumer.ReceivedAsync += async (bc, ea) => await processMessage(ea).ConfigureAwait(false);
- channel.BasicConsume(queue: TestQueueName, autoAck: true, consumer: consumer);
+ await channel.BasicConsumeAsync(queue: TestQueueName, autoAck: true, consumer: consumer).ConfigureAwait(false);
}
public static void AddMessagingTags(Activity? activity)
diff --git a/examples/MicroserviceExample/Utils/Utils.csproj b/examples/MicroserviceExample/Utils/Utils.csproj
index 54d07243470..1c95380e4c9 100644
--- a/examples/MicroserviceExample/Utils/Utils.csproj
+++ b/examples/MicroserviceExample/Utils/Utils.csproj
@@ -1,6 +1,6 @@
- netstandard2.0
+ $(DefaultTargetFrameworkForExampleApps)
diff --git a/examples/MicroserviceExample/WebApi/Controllers/SendMessageController.cs b/examples/MicroserviceExample/WebApi/Controllers/SendMessageController.cs
index 6a8a5f9564a..e08009c5f8e 100644
--- a/examples/MicroserviceExample/WebApi/Controllers/SendMessageController.cs
+++ b/examples/MicroserviceExample/WebApi/Controllers/SendMessageController.cs
@@ -10,18 +10,14 @@ namespace WebApi.Controllers;
[Route("[controller]")]
public class SendMessageController : ControllerBase
{
- private readonly ILogger logger;
private readonly MessageSender messageSender;
- public SendMessageController(ILogger logger, MessageSender messageSender)
+ public SendMessageController(MessageSender messageSender)
{
- this.logger = logger;
this.messageSender = messageSender;
}
[HttpGet]
- public string Get()
- {
- return this.messageSender.SendMessage();
- }
+ public async Task Get() =>
+ await this.messageSender.SendMessageAsync().ConfigureAwait(false);
}
diff --git a/examples/MicroserviceExample/WebApi/Dockerfile b/examples/MicroserviceExample/WebApi/Dockerfile
index d74077a0a87..a11bc474dcf 100644
--- a/examples/MicroserviceExample/WebApi/Dockerfile
+++ b/examples/MicroserviceExample/WebApi/Dockerfile
@@ -1,12 +1,18 @@
-ARG SDK_VERSION=8.0
-FROM mcr.microsoft.com/dotnet/sdk:${SDK_VERSION} AS build
+ARG SDK_VERSION=9.0
+FROM mcr.microsoft.com/dotnet/sdk:8.0.414@sha256:3cef19377b2ef2a0171e930a24627677447c3e41b5f2eab84ff4895f1b15d254 AS dotnet-sdk-8.0
+FROM mcr.microsoft.com/dotnet/sdk:9.0.305@sha256:bb42ae2c058609d1746baf24fe6864ecab0686711dfca1f4b7a99e367ab17162 AS dotnet-sdk-9.0
+
+FROM dotnet-sdk-${SDK_VERSION} AS build
ARG PUBLISH_CONFIGURATION=Release
-ARG PUBLISH_FRAMEWORK=net8.0
+ARG PUBLISH_FRAMEWORK=net9.0
WORKDIR /app
COPY . ./
RUN dotnet publish ./examples/MicroserviceExample/WebApi -c "${PUBLISH_CONFIGURATION}" -f "${PUBLISH_FRAMEWORK}" -o /out -p:IntegrationBuild=true
-FROM mcr.microsoft.com/dotnet/aspnet:${SDK_VERSION} AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.20@sha256:e88f90b6d9fd7e9e0d8e231d068fccdbebd3c91892441a85ef35066aea9a4e1e AS dotnet-aspnet-8.0
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.9@sha256:1af4114db9ba87542a3f23dbb5cd9072cad7fcc8505f6e9131d1feb580286a6f AS dotnet-aspnet-9.0
+
+FROM dotnet-aspnet-${SDK_VERSION} AS runtime
WORKDIR /app
COPY --from=build /out ./
ENTRYPOINT ["dotnet", "WebApi.dll"]
diff --git a/examples/MicroserviceExample/WebApi/Program.cs b/examples/MicroserviceExample/WebApi/Program.cs
index 9ed5a0b9c01..2dbc341f6d4 100644
--- a/examples/MicroserviceExample/WebApi/Program.cs
+++ b/examples/MicroserviceExample/WebApi/Program.cs
@@ -1,19 +1,36 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-namespace WebApi;
+using OpenTelemetry.Trace;
+using Utils.Messaging;
-public class Program
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddControllers();
+
+builder.Services.AddSingleton();
+
+builder.Services.AddOpenTelemetry()
+ .WithTracing(b => b
+ .AddAspNetCoreInstrumentation()
+ .AddSource(nameof(MessageSender))
+ .AddZipkinExporter(o =>
+ {
+ var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
+ o.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
+ }));
+
+builder.WebHost.UseUrls("http://*:5000");
+
+var app = builder.Build();
+
+if (app.Environment.IsDevelopment())
{
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
-
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseUrls("http://*:5000").UseStartup();
- });
+ app.UseDeveloperExceptionPage();
}
+
+app.UseRouting();
+
+app.MapControllers();
+
+app.Run();
diff --git a/examples/MicroserviceExample/WebApi/Startup.cs b/examples/MicroserviceExample/WebApi/Startup.cs
deleted file mode 100644
index b77ea597c73..00000000000
--- a/examples/MicroserviceExample/WebApi/Startup.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using OpenTelemetry.Trace;
-using Utils.Messaging;
-
-namespace WebApi;
-
-public class Startup
-{
- public Startup(IConfiguration configuration)
- {
- this.Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddControllers();
-
- services.AddSingleton();
-
- services.AddOpenTelemetry()
- .WithTracing(builder => builder
- .AddAspNetCoreInstrumentation()
- .AddSource(nameof(MessageSender))
- .AddZipkinExporter(b =>
- {
- var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
- b.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
- }));
- }
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseRouting();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
-}
diff --git a/examples/MicroserviceExample/WebApi/WebApi.csproj b/examples/MicroserviceExample/WebApi/WebApi.csproj
index 2d5846e580f..9c7ae7cf936 100644
--- a/examples/MicroserviceExample/WebApi/WebApi.csproj
+++ b/examples/MicroserviceExample/WebApi/WebApi.csproj
@@ -1,11 +1,11 @@
-
+
$(DefaultTargetFrameworkForExampleApps)
+ $(NoWarn);CA1515
-
diff --git a/examples/MicroserviceExample/WorkerService/Dockerfile b/examples/MicroserviceExample/WorkerService/Dockerfile
index dafc0049b46..07dde87f8b9 100644
--- a/examples/MicroserviceExample/WorkerService/Dockerfile
+++ b/examples/MicroserviceExample/WorkerService/Dockerfile
@@ -1,12 +1,19 @@
-ARG SDK_VERSION=8.0
-FROM mcr.microsoft.com/dotnet/sdk:${SDK_VERSION} AS build
+ARG SDK_VERSION=9.0
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0.414@sha256:3cef19377b2ef2a0171e930a24627677447c3e41b5f2eab84ff4895f1b15d254 AS dotnet-sdk-8.0
+FROM mcr.microsoft.com/dotnet/sdk:9.0.305@sha256:bb42ae2c058609d1746baf24fe6864ecab0686711dfca1f4b7a99e367ab17162 AS dotnet-sdk-9.0
+
+FROM dotnet-sdk-${SDK_VERSION} AS build
ARG PUBLISH_CONFIGURATION=Release
-ARG PUBLISH_FRAMEWORK=net8.0
+ARG PUBLISH_FRAMEWORK=net9.0
WORKDIR /app
COPY . ./
RUN dotnet publish ./examples/MicroserviceExample/WorkerService -c "${PUBLISH_CONFIGURATION}" -f "${PUBLISH_FRAMEWORK}" -o /out -p:IntegrationBuild=true
-FROM mcr.microsoft.com/dotnet/aspnet:${SDK_VERSION} AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.20@sha256:e88f90b6d9fd7e9e0d8e231d068fccdbebd3c91892441a85ef35066aea9a4e1e AS dotnet-aspnet-8.0
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.9@sha256:1af4114db9ba87542a3f23dbb5cd9072cad7fcc8505f6e9131d1feb580286a6f AS dotnet-aspnet-9.0
+
+FROM dotnet-aspnet-${SDK_VERSION} AS runtime
WORKDIR /app
COPY --from=build /out ./
ENTRYPOINT ["dotnet", "WorkerService.dll"]
diff --git a/examples/MicroserviceExample/WorkerService/Program.cs b/examples/MicroserviceExample/WorkerService/Program.cs
index 24e03322c12..118484d827b 100644
--- a/examples/MicroserviceExample/WorkerService/Program.cs
+++ b/examples/MicroserviceExample/WorkerService/Program.cs
@@ -3,31 +3,22 @@
using OpenTelemetry.Trace;
using Utils.Messaging;
+using WorkerService;
-namespace WorkerService;
+var builder = Host.CreateApplicationBuilder(args);
-public class Program
-{
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
+builder.Services.AddHostedService();
+builder.Services.AddSingleton();
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureServices((hostContext, services) =>
- {
- services.AddHostedService();
+builder.Services.AddOpenTelemetry()
+ .WithTracing(b => b
+ .AddSource(nameof(MessageReceiver))
+ .AddZipkinExporter(o =>
+ {
+ var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
+ o.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
+ }));
- services.AddSingleton();
+var app = builder.Build();
- services.AddOpenTelemetry()
- .WithTracing(builder => builder
- .AddSource(nameof(MessageReceiver))
- .AddZipkinExporter(b =>
- {
- var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
- b.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
- }));
- });
-}
+app.Run();
diff --git a/examples/MicroserviceExample/WorkerService/Worker.cs b/examples/MicroserviceExample/WorkerService/Worker.cs
index 9b3fa484d19..1292076399b 100644
--- a/examples/MicroserviceExample/WorkerService/Worker.cs
+++ b/examples/MicroserviceExample/WorkerService/Worker.cs
@@ -5,7 +5,7 @@
namespace WorkerService;
-public partial class Worker : BackgroundService
+internal sealed class Worker : BackgroundService
{
private readonly MessageReceiver messageReceiver;
@@ -14,22 +14,10 @@ public Worker(MessageReceiver messageReceiver)
this.messageReceiver = messageReceiver;
}
- public override Task StartAsync(CancellationToken cancellationToken)
- {
- return base.StartAsync(cancellationToken);
- }
-
- public override async Task StopAsync(CancellationToken cancellationToken)
- {
- await base.StopAsync(cancellationToken).ConfigureAwait(false);
- }
-
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
stoppingToken.ThrowIfCancellationRequested();
- this.messageReceiver.StartConsumer();
-
- await Task.CompletedTask.ConfigureAwait(false);
+ await this.messageReceiver.StartConsumerAsync().ConfigureAwait(false);
}
}
diff --git a/examples/MicroserviceExample/WorkerService/WorkerService.csproj b/examples/MicroserviceExample/WorkerService/WorkerService.csproj
index b9b1a680772..a1dc2dae9f4 100644
--- a/examples/MicroserviceExample/WorkerService/WorkerService.csproj
+++ b/examples/MicroserviceExample/WorkerService/WorkerService.csproj
@@ -1,15 +1,14 @@
$(DefaultTargetFrameworkForExampleApps)
+ $(NoWarn);CA1812
-
-
diff --git a/examples/MicroserviceExample/docker-compose.yml b/examples/MicroserviceExample/docker-compose.yml
index 35385fae591..ebbe2bbf345 100644
--- a/examples/MicroserviceExample/docker-compose.yml
+++ b/examples/MicroserviceExample/docker-compose.yml
@@ -1,13 +1,11 @@
-version: '3.8'
-
services:
zipkin:
- image: openzipkin/zipkin
+ image: openzipkin/zipkin:3.5.1@sha256:bb570eb45c2994eaf32da783cc098b3d51d1095b73ec92919863d73d0a9eaafb
ports:
- 9411:9411
rabbitmq:
- image: rabbitmq:3-management-alpine
+ image: rabbitmq:4-management-alpine@sha256:603089229e6060279f1b5db7fb5d844d1f259dd7a73fca8e3b15bb93713ad27c
ports:
- 5672:5672
- 15672:15672
diff --git a/global.json b/global.json
index 271c2d4e2c8..8c14264b61d 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
"rollForward": "latestFeature",
- "version": "9.0.102"
+ "version": "9.0.305"
}
}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 93113cdc8e2..dfe83531769 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,3 +1,4 @@
+
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/AssemblyInfo.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/AssemblyInfo.cs
deleted file mode 100644
index 0c163966efc..00000000000
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/AssemblyInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("OpenTelemetry" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Console" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
-
-#if SIGNED
-file static class AssemblyInfo
-{
- public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
-}
-#else
-file static class AssemblyInfo
-{
- public const string PublicKey = "";
-}
-#endif
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md b/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md
index 3b751248ee4..4cad7a9486d 100644
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md
+++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md
@@ -7,6 +7,18 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs
index 4a96f1b0818..51a1af5c5c5 100644
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs
@@ -36,7 +36,7 @@ public static LoggerProviderBuilder AddInstrumentation<
loggerProviderBuilder.ConfigureBuilder((sp, builder) =>
{
- builder.AddInstrumentation(() => sp.GetRequiredService());
+ builder.AddInstrumentation(sp.GetRequiredService);
});
return loggerProviderBuilder;
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs
index fea2275e9a3..ff52ee37cb3 100644
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs
@@ -36,7 +36,7 @@ public static MeterProviderBuilder AddInstrumentation<
meterProviderBuilder.ConfigureBuilder((sp, builder) =>
{
- builder.AddInstrumentation(() => sp.GetRequiredService());
+ builder.AddInstrumentation(sp.GetRequiredService);
});
return meterProviderBuilder;
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/OpenTelemetry.Api.ProviderBuilderExtensions.csproj b/src/OpenTelemetry.Api.ProviderBuilderExtensions/OpenTelemetry.Api.ProviderBuilderExtensions.csproj
index d057d595f43..8c28b1e5c44 100644
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/OpenTelemetry.Api.ProviderBuilderExtensions.csproj
+++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/OpenTelemetry.Api.ProviderBuilderExtensions.csproj
@@ -5,7 +5,6 @@
Contains extensions to register OpenTelemetry in applications using Microsoft.Extensions.DependencyInjection
OpenTelemetry
core-
- latest-all
@@ -16,4 +15,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Trace/OpenTelemetryDependencyInjectionTracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Trace/OpenTelemetryDependencyInjectionTracerProviderBuilderExtensions.cs
index 2e6ab37628d..939fd34b366 100644
--- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Trace/OpenTelemetryDependencyInjectionTracerProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Trace/OpenTelemetryDependencyInjectionTracerProviderBuilderExtensions.cs
@@ -36,7 +36,7 @@ public static TracerProviderBuilder AddInstrumentation<
tracerProviderBuilder.ConfigureBuilder((sp, builder) =>
{
- builder.AddInstrumentation(() => sp.GetRequiredService());
+ builder.AddInstrumentation(sp.GetRequiredService);
});
return tracerProviderBuilder;
diff --git a/src/OpenTelemetry.Api/.publicApi/Experimental/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Api/.publicApi/Experimental/PublicAPI.Unshipped.txt
index 4cb12fd2969..e4f616a9dca 100644
--- a/src/OpenTelemetry.Api/.publicApi/Experimental/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry.Api/.publicApi/Experimental/PublicAPI.Unshipped.txt
@@ -1,73 +1,75 @@
-abstract OpenTelemetry.Logs.Logger.EmitLog(in OpenTelemetry.Logs.LogRecordData data, in OpenTelemetry.Logs.LogRecordAttributeList attributes) -> void
-OpenTelemetry.Logs.Logger
-OpenTelemetry.Logs.Logger.EmitLog(in OpenTelemetry.Logs.LogRecordData data) -> void
-OpenTelemetry.Logs.Logger.Logger(string? name) -> void
-OpenTelemetry.Logs.Logger.Name.get -> string!
-OpenTelemetry.Logs.Logger.Version.get -> string?
-OpenTelemetry.Logs.LoggerProvider.GetLogger() -> OpenTelemetry.Logs.Logger!
-OpenTelemetry.Logs.LoggerProvider.GetLogger(string? name) -> OpenTelemetry.Logs.Logger!
-OpenTelemetry.Logs.LoggerProvider.GetLogger(string? name, string? version) -> OpenTelemetry.Logs.Logger!
-OpenTelemetry.Logs.LogRecordAttributeList
-OpenTelemetry.Logs.LogRecordAttributeList.Add(string! key, object? value) -> void
-OpenTelemetry.Logs.LogRecordAttributeList.Add(System.Collections.Generic.KeyValuePair attribute) -> void
-OpenTelemetry.Logs.LogRecordAttributeList.Clear() -> void
-OpenTelemetry.Logs.LogRecordAttributeList.Count.get -> int
-OpenTelemetry.Logs.LogRecordAttributeList.Enumerator
-OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Current.get -> System.Collections.Generic.KeyValuePair
-OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Dispose() -> void
-OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Enumerator() -> void
-OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.MoveNext() -> bool
-OpenTelemetry.Logs.LogRecordAttributeList.GetEnumerator() -> OpenTelemetry.Logs.LogRecordAttributeList.Enumerator
-OpenTelemetry.Logs.LogRecordAttributeList.LogRecordAttributeList() -> void
-OpenTelemetry.Logs.LogRecordAttributeList.RecordException(System.Exception! exception) -> void
-OpenTelemetry.Logs.LogRecordAttributeList.this[int index].get -> System.Collections.Generic.KeyValuePair
-OpenTelemetry.Logs.LogRecordAttributeList.this[int index].set -> void
-OpenTelemetry.Logs.LogRecordAttributeList.this[string! key].set -> void
-OpenTelemetry.Logs.LogRecordData
-OpenTelemetry.Logs.LogRecordData.Body.get -> string?
-OpenTelemetry.Logs.LogRecordData.Body.set -> void
-OpenTelemetry.Logs.LogRecordData.LogRecordData() -> void
-OpenTelemetry.Logs.LogRecordData.LogRecordData(in System.Diagnostics.ActivityContext activityContext) -> void
-OpenTelemetry.Logs.LogRecordData.LogRecordData(System.Diagnostics.Activity? activity) -> void
-OpenTelemetry.Logs.LogRecordData.Severity.get -> OpenTelemetry.Logs.LogRecordSeverity?
-OpenTelemetry.Logs.LogRecordData.Severity.set -> void
-OpenTelemetry.Logs.LogRecordData.SeverityText.get -> string?
-OpenTelemetry.Logs.LogRecordData.SeverityText.set -> void
-OpenTelemetry.Logs.LogRecordData.SpanId.get -> System.Diagnostics.ActivitySpanId
-OpenTelemetry.Logs.LogRecordData.SpanId.set -> void
-OpenTelemetry.Logs.LogRecordData.Timestamp.get -> System.DateTime
-OpenTelemetry.Logs.LogRecordData.Timestamp.set -> void
-OpenTelemetry.Logs.LogRecordData.TraceFlags.get -> System.Diagnostics.ActivityTraceFlags
-OpenTelemetry.Logs.LogRecordData.TraceFlags.set -> void
-OpenTelemetry.Logs.LogRecordData.TraceId.get -> System.Diagnostics.ActivityTraceId
-OpenTelemetry.Logs.LogRecordData.TraceId.set -> void
-OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Debug = 5 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Debug2 = 6 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Debug3 = 7 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Debug4 = 8 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Error = 17 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Error2 = 18 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Error3 = 19 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Error4 = 20 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Fatal = 21 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Fatal2 = 22 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Fatal3 = 23 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Fatal4 = 24 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Info = 9 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Info2 = 10 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Info3 = 11 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Info4 = 12 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Trace = 1 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Trace2 = 2 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Trace3 = 3 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Trace4 = 4 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Unspecified = 0 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Warn = 13 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Warn2 = 14 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Warn3 = 15 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverity.Warn4 = 16 -> OpenTelemetry.Logs.LogRecordSeverity
-OpenTelemetry.Logs.LogRecordSeverityExtensions
-static OpenTelemetry.Logs.LogRecordAttributeList.CreateFromEnumerable(System.Collections.Generic.IEnumerable>! attributes) -> OpenTelemetry.Logs.LogRecordAttributeList
-static OpenTelemetry.Logs.LogRecordSeverityExtensions.ToShortName(this OpenTelemetry.Logs.LogRecordSeverity logRecordSeverity) -> string!
-virtual OpenTelemetry.Logs.LoggerProvider.TryCreateLogger(string? name, out OpenTelemetry.Logs.Logger? logger) -> bool
+[OTEL1001]abstract OpenTelemetry.Logs.Logger.EmitLog(in OpenTelemetry.Logs.LogRecordData data, in OpenTelemetry.Logs.LogRecordAttributeList attributes) -> void
+[OTEL1001]OpenTelemetry.Logs.Logger
+[OTEL1001]OpenTelemetry.Logs.Logger.EmitLog(in OpenTelemetry.Logs.LogRecordData data) -> void
+[OTEL1001]OpenTelemetry.Logs.Logger.Logger(string? name) -> void
+[OTEL1001]OpenTelemetry.Logs.Logger.Name.get -> string!
+[OTEL1001]OpenTelemetry.Logs.Logger.Version.get -> string?
+[OTEL1001]OpenTelemetry.Logs.LoggerProvider.GetLogger() -> OpenTelemetry.Logs.Logger!
+[OTEL1001]OpenTelemetry.Logs.LoggerProvider.GetLogger(string? name) -> OpenTelemetry.Logs.Logger!
+[OTEL1001]OpenTelemetry.Logs.LoggerProvider.GetLogger(string? name, string? version) -> OpenTelemetry.Logs.Logger!
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Add(string! key, object? value) -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Add(System.Collections.Generic.KeyValuePair attribute) -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Clear() -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Count.get -> int
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Enumerator
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Current.get -> System.Collections.Generic.KeyValuePair
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Dispose() -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.Enumerator() -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.Enumerator.MoveNext() -> bool
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.GetEnumerator() -> OpenTelemetry.Logs.LogRecordAttributeList.Enumerator
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.LogRecordAttributeList() -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.RecordException(System.Exception! exception) -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.this[int index].get -> System.Collections.Generic.KeyValuePair
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.this[int index].set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordAttributeList.this[string! key].set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Body.get -> string?
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Body.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.LogRecordData() -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.LogRecordData(in System.Diagnostics.ActivityContext activityContext) -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.LogRecordData(System.Diagnostics.Activity? activity) -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Severity.get -> OpenTelemetry.Logs.LogRecordSeverity?
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Severity.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.SeverityText.get -> string?
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.SeverityText.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.SpanId.get -> System.Diagnostics.ActivitySpanId
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.SpanId.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Timestamp.get -> System.DateTime
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.Timestamp.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.TraceFlags.get -> System.Diagnostics.ActivityTraceFlags
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.TraceFlags.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.TraceId.get -> System.Diagnostics.ActivityTraceId
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.TraceId.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.EventName.get -> string?
+[OTEL1001]OpenTelemetry.Logs.LogRecordData.EventName.set -> void
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Debug = 5 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Debug2 = 6 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Debug3 = 7 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Debug4 = 8 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Error = 17 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Error2 = 18 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Error3 = 19 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Error4 = 20 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Fatal = 21 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Fatal2 = 22 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Fatal3 = 23 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Fatal4 = 24 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Info = 9 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Info2 = 10 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Info3 = 11 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Info4 = 12 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Trace = 1 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Trace2 = 2 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Trace3 = 3 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Trace4 = 4 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Unspecified = 0 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Warn = 13 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Warn2 = 14 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Warn3 = 15 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverity.Warn4 = 16 -> OpenTelemetry.Logs.LogRecordSeverity
+[OTEL1001]OpenTelemetry.Logs.LogRecordSeverityExtensions
+[OTEL1001]static OpenTelemetry.Logs.LogRecordAttributeList.CreateFromEnumerable(System.Collections.Generic.IEnumerable>! attributes) -> OpenTelemetry.Logs.LogRecordAttributeList
+[OTEL1001]static OpenTelemetry.Logs.LogRecordSeverityExtensions.ToShortName(this OpenTelemetry.Logs.LogRecordSeverity logRecordSeverity) -> string!
+[OTEL1001]virtual OpenTelemetry.Logs.LoggerProvider.TryCreateLogger(string? name, out OpenTelemetry.Logs.Logger? logger) -> bool
diff --git a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt
index 73362dee3c1..a0742cbc241 100644
--- a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt
+++ b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt
@@ -126,6 +126,8 @@ OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, OpenTelemetry.Trace.Spa
OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan!
OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp) -> OpenTelemetry.Trace.TelemetrySpan!
OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name) -> OpenTelemetry.Trace.TelemetrySpan!
+OpenTelemetry.Trace.TelemetrySpan.AddLink(OpenTelemetry.Trace.SpanContext spanContext, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan!
+OpenTelemetry.Trace.TelemetrySpan.AddLink(OpenTelemetry.Trace.SpanContext spanContext) -> OpenTelemetry.Trace.TelemetrySpan!
OpenTelemetry.Trace.TelemetrySpan.Context.get -> OpenTelemetry.Trace.SpanContext
OpenTelemetry.Trace.TelemetrySpan.Dispose() -> void
OpenTelemetry.Trace.TelemetrySpan.End() -> void
@@ -151,7 +153,8 @@ OpenTelemetry.Trace.Tracer.StartRootSpan(string! name, OpenTelemetry.Trace.SpanK
OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, in OpenTelemetry.Trace.SpanContext parentContext = default(OpenTelemetry.Trace.SpanContext), OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan!
OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind, in OpenTelemetry.Trace.TelemetrySpan? parentSpan, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan!
OpenTelemetry.Trace.TracerProvider
-OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version = null) -> OpenTelemetry.Trace.Tracer!
+OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version = null, System.Collections.Generic.IEnumerable>? tags = null) -> OpenTelemetry.Trace.Tracer!
+OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version) -> OpenTelemetry.Trace.Tracer!
OpenTelemetry.Trace.TracerProvider.TracerProvider() -> void
OpenTelemetry.Trace.TracerProviderBuilder
OpenTelemetry.Trace.TracerProviderBuilder.TracerProviderBuilder() -> void
diff --git a/src/OpenTelemetry.Api/AssemblyInfo.cs b/src/OpenTelemetry.Api/AssemblyInfo.cs
deleted file mode 100644
index b1e95ff67e6..00000000000
--- a/src/OpenTelemetry.Api/AssemblyInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("OpenTelemetry" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Api.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Console" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.InMemory" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Shims.OpenTracing.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
-
-#if SIGNED
-file static class AssemblyInfo
-{
- public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
-}
-#else
-file static class AssemblyInfo
-{
- public const string PublicKey = "";
-}
-#endif
diff --git a/src/OpenTelemetry.Api/Baggage.cs b/src/OpenTelemetry.Api/Baggage.cs
index 93b972f4f17..d69e0170ca4 100644
--- a/src/OpenTelemetry.Api/Baggage.cs
+++ b/src/OpenTelemetry.Api/Baggage.cs
@@ -16,7 +16,7 @@ namespace OpenTelemetry;
public readonly struct Baggage : IEquatable
{
private static readonly RuntimeContextSlot RuntimeContextSlot = RuntimeContext.RegisterSlot("otel.baggage");
- private static readonly Dictionary EmptyBaggage = new();
+ private static readonly Dictionary EmptyBaggage = [];
private readonly Dictionary baggage;
@@ -305,7 +305,9 @@ public Baggage RemoveBaggage(string name)
/// Returns a new with all the key/value pairs removed.
///
/// New with all the key/value pairs removed.
+#pragma warning disable CA1822 // Mark members as static
public Baggage ClearBaggage()
+#pragma warning restore CA1822 // Mark members as static
=> default;
///
@@ -343,7 +345,11 @@ public override int GetHashCode()
unchecked
{
hash = (hash * 23) + baggage.Comparer.GetHashCode(item.Key);
+#if NET
+ hash = (hash * 23) + item.Value.GetHashCode(StringComparison.Ordinal);
+#else
hash = (hash * 23) + item.Value.GetHashCode();
+#endif
}
}
diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md
index a726bf04cc5..961b11de5b0 100644
--- a/src/OpenTelemetry.Api/CHANGELOG.md
+++ b/src/OpenTelemetry.Api/CHANGELOG.md
@@ -6,6 +6,35 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+* Added `AddLink(SpanContext, SpanAttributes?)` to `TelemetrySpan` to support
+ linking spans and associating optional attributes for advanced trace relationships.
+ ([#6305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6305))
+
+* Experimental (only in pre-release versions): Added the `EventName` property
+ to `LogRecordData`
+ ([#6306](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6306))
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+* Added a new overload for `TracerProvider.GetTracer` which accepts an optional
+ `IEnumerable>? tags` parameter, allowing
+ additional attributes to be associated with the `Tracer`.
+ ([#6137](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6137))
+
+## 1.11.2
+
+Released 2025-Mar-04
+
+* Revert optimize performance of `TraceContextPropagator.Extract` introduced
+ in #5749 to resolve [GHSA-8785-wc3w-h8q6](https://github.com/open-telemetry/opentelemetry-dotnet/security/advisories/GHSA-8785-wc3w-h8q6).
+ ([#6161](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6161))
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Api/Context/Propagation/B3Propagator.cs b/src/OpenTelemetry.Api/Context/Propagation/B3Propagator.cs
index 18276172106..56c353f4a28 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/B3Propagator.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/B3Propagator.cs
@@ -27,6 +27,7 @@ public sealed class B3Propagator : TextMapPropagator
internal const string UpperTraceId = "0000000000000000";
// Sampled values via the X_B3_SAMPLED header.
+ internal const char SampledValueChar = '1';
internal const string SampledValue = "1";
// Some old zipkin implementations may send true/false for the sampled header. Only use this for checking incoming values.
@@ -35,7 +36,7 @@ public sealed class B3Propagator : TextMapPropagator
// "Debug" sampled value.
internal const string FlagsValue = "1";
- private static readonly HashSet AllFields = new() { XB3TraceId, XB3SpanId, XB3ParentSpanId, XB3Sampled, XB3Flags };
+ private static readonly HashSet AllFields = [XB3TraceId, XB3SpanId, XB3ParentSpanId, XB3Sampled, XB3Flags];
private static readonly HashSet SampledValues = new(StringComparer.Ordinal) { SampledValue, LegacySampledValue };
@@ -130,7 +131,7 @@ public override void Inject(PropagationContext context, T carrier, Action
public override ISet Fields => new HashSet { BaggageHeaderName };
@@ -52,7 +52,7 @@ public override PropagationContext Extract(PropagationContext context, T carr
var baggageCollection = getter(carrier, BaggageHeaderName);
if (baggageCollection?.Any() ?? false)
{
- if (TryExtractBaggage(baggageCollection.ToArray(), out var baggage))
+ if (TryExtractBaggage([.. baggageCollection], out var baggage))
{
return new PropagationContext(context.ActivityContext, new Baggage(baggage!));
}
@@ -138,7 +138,11 @@ internal static bool TryExtractBaggage(
break;
}
+#if NET
+ if (pair.IndexOf('=', StringComparison.Ordinal) < 0)
+#else
if (pair.IndexOf('=') < 0)
+#endif
{
continue;
}
@@ -157,10 +161,7 @@ internal static bool TryExtractBaggage(
continue;
}
- if (baggageDictionary == null)
- {
- baggageDictionary = new Dictionary();
- }
+ baggageDictionary ??= [];
baggageDictionary[key] = value;
}
diff --git a/src/OpenTelemetry.Api/Context/Propagation/CompositeTextMapPropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/CompositeTextMapPropagator.cs
index e160bc8ac78..d0375aadc71 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/CompositeTextMapPropagator.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/CompositeTextMapPropagator.cs
@@ -11,7 +11,7 @@ namespace OpenTelemetry.Context.Propagation;
///
public class CompositeTextMapPropagator : TextMapPropagator
{
- private readonly IReadOnlyList propagators;
+ private readonly List propagators;
private readonly ISet allFields;
///
diff --git a/src/OpenTelemetry.Api/Context/Propagation/NoopTextMapPropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/NoopTextMapPropagator.cs
index a722a4f087a..0721ad52883 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/NoopTextMapPropagator.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/NoopTextMapPropagator.cs
@@ -5,7 +5,9 @@ namespace OpenTelemetry.Context.Propagation;
internal sealed class NoopTextMapPropagator : TextMapPropagator
{
+#pragma warning disable CA1805 // Do not initialize unnecessarily
private static readonly PropagationContext DefaultPropagationContext = default;
+#pragma warning restore CA1805 // Do not initialize unnecessarily
public override ISet? Fields => null;
diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
index ee67aaaaafb..a00f8e8beb1 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
@@ -1,9 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+using System.Text;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Context.Propagation;
@@ -16,6 +16,12 @@ public class TraceContextPropagator : TextMapPropagator
private const string TraceParent = "traceparent";
private const string TraceState = "tracestate";
+ // The following length limits are from Trace Context v1 https://www.w3.org/TR/trace-context-1/#key
+ private const int TraceStateKeyMaxLength = 256;
+ private const int TraceStateKeyTenantMaxLength = 241;
+ private const int TraceStateKeyVendorMaxLength = 14;
+ private const int TraceStateValueMaxLength = 256;
+
private static readonly int VersionPrefixIdLength = "00-".Length;
private static readonly int TraceIdLength = "0af7651916cd43dd8448eb211c80319c".Length;
private static readonly int VersionAndTraceIdLength = "00-0af7651916cd43dd8448eb211c80319c-".Length;
@@ -24,12 +30,6 @@ public class TraceContextPropagator : TextMapPropagator
private static readonly int OptionsLength = "00".Length;
private static readonly int TraceparentLengthV0 = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-00".Length;
- // The following length limits are from Trace Context v1 https://www.w3.org/TR/trace-context-1/#key
- private static readonly int TraceStateKeyMaxLength = 256;
- private static readonly int TraceStateKeyTenantMaxLength = 241;
- private static readonly int TraceStateKeyVendorMaxLength = 14;
- private static readonly int TraceStateValueMaxLength = 256;
-
///
public override ISet Fields => new HashSet { TraceState, TraceParent };
@@ -76,7 +76,7 @@ public override PropagationContext Extract(PropagationContext context, T carr
var tracestateCollection = getter(carrier, TraceState);
if (tracestateCollection?.Any() ?? false)
{
- TryExtractTracestate(tracestateCollection, out tracestate);
+ TryExtractTracestate([.. tracestateCollection], out tracestate);
}
return new PropagationContext(
@@ -173,7 +173,7 @@ internal static bool TryExtractTraceparent(string traceparent, out ActivityTrace
try
{
- traceId = ActivityTraceId.CreateFromString(traceparent.AsSpan().Slice(VersionPrefixIdLength, TraceIdLength));
+ traceId = ActivityTraceId.CreateFromString(traceparent.AsSpan(VersionPrefixIdLength, TraceIdLength));
}
catch (ArgumentOutOfRangeException)
{
@@ -189,7 +189,7 @@ internal static bool TryExtractTraceparent(string traceparent, out ActivityTrace
byte optionsLowByte;
try
{
- spanId = ActivitySpanId.CreateFromString(traceparent.AsSpan().Slice(VersionAndTraceIdLength, SpanIdLength));
+ spanId = ActivitySpanId.CreateFromString(traceparent.AsSpan(VersionAndTraceIdLength, SpanIdLength));
_ = HexCharToByte(traceparent[VersionAndTraceIdAndSpanIdLength]); // to verify if there is no bad chars on options position
optionsLowByte = HexCharToByte(traceparent[VersionAndTraceIdAndSpanIdLength + 1]);
}
@@ -220,37 +220,31 @@ internal static bool TryExtractTraceparent(string traceparent, out ActivityTrace
return true;
}
- internal static bool TryExtractTracestate(IEnumerable tracestateCollection, out string tracestateResult)
+ internal static bool TryExtractTracestate(string[] tracestateCollection, out string tracestateResult)
{
tracestateResult = string.Empty;
- char[]? rentedArray = null;
- Span traceStateBuffer = stackalloc char[128]; // 256B
- Span keyLookupBuffer = stackalloc char[96]; // 192B (3x32 keys)
- int keys = 0;
- int charsWritten = 0;
-
- try
+ if (tracestateCollection != null)
{
- foreach (var tracestateItem in tracestateCollection)
+ var keySet = new HashSet();
+ var result = new StringBuilder();
+ for (int i = 0; i < tracestateCollection.Length; ++i)
{
- var tracestate = tracestateItem.AsSpan();
- int position = 0;
-
- while (position < tracestate.Length)
+ var tracestate = tracestateCollection[i].AsSpan();
+ int begin = 0;
+ while (begin < tracestate.Length)
{
- int length = tracestate.Slice(position).IndexOf(',');
+ int length = tracestate.Slice(begin).IndexOf(',');
ReadOnlySpan listMember;
-
if (length != -1)
{
- listMember = tracestate.Slice(position, length).Trim();
- position += length + 1;
+ listMember = tracestate.Slice(begin, length).Trim();
+ begin += length + 1;
}
else
{
- listMember = tracestate.Slice(position).Trim();
- position = tracestate.Length;
+ listMember = tracestate.Slice(begin).Trim();
+ begin = tracestate.Length;
}
// https://github.com/w3c/trace-context/blob/master/spec/20-http_request_header_format.md#tracestate-header-field-values
@@ -261,7 +255,7 @@ internal static bool TryExtractTracestate(IEnumerable tracestateCollecti
continue;
}
- if (keys >= 32)
+ if (keySet.Count >= 32)
{
// https://github.com/w3c/trace-context/blob/master/spec/20-http_request_header_format.md#list
// test_tracestate_member_count_limit
@@ -292,107 +286,29 @@ internal static bool TryExtractTracestate(IEnumerable tracestateCollecti
}
// ValidateKey() call above has ensured the key does not contain upper case letters.
-
- var duplicationCheckLength = Math.Min(key.Length, 3);
-
- if (keys > 0)
- {
- // Fast path check of first three chars for potential duplicated keys
- var potentialMatchingKeyPosition = 1;
- var found = false;
- for (int i = 0; i < keys * 3; i += 3)
- {
- if (keyLookupBuffer.Slice(i, duplicationCheckLength).SequenceEqual(key.Slice(0, duplicationCheckLength)))
- {
- found = true;
- break;
- }
-
- potentialMatchingKeyPosition++;
- }
-
- // If the fast check has found a possible duplicate, we need to do a full check
- if (found)
- {
- var bufferToCompare = traceStateBuffer.Slice(0, charsWritten);
-
- // We know which key is the first possible duplicate, so skip to that key
- // by slicing to the position after the appropriate comma.
- for (int i = 1; i < potentialMatchingKeyPosition; i++)
- {
- var commaIndex = bufferToCompare.IndexOf(',');
-
- if (commaIndex > -1)
- {
- bufferToCompare.Slice(commaIndex);
- }
- }
-
- int existingIndex = -1;
- while ((existingIndex = bufferToCompare.IndexOf(key)) > -1)
- {
- if ((existingIndex > 0 && bufferToCompare[existingIndex - 1] != ',') || bufferToCompare[existingIndex + key.Length] != '=')
- {
- continue; // this is not a key
- }
-
- return false; // test_tracestate_duplicated_keys
- }
- }
- }
-
- // Store up to the first three characters of the key for use in the duplicate lookup fast path
- var startKeyLookupIndex = keys > 0 ? keys * 3 : 0;
- key.Slice(0, duplicationCheckLength).CopyTo(keyLookupBuffer.Slice(startKeyLookupIndex));
-
- // Check we have capacity to write the key and value
- var requiredCapacity = charsWritten > 0 ? listMember.Length + 1 : listMember.Length;
-
- while (charsWritten + requiredCapacity > traceStateBuffer.Length)
+ if (!keySet.Add(key.ToString()))
{
- GrowBuffer(ref rentedArray, ref traceStateBuffer);
+ // test_tracestate_duplicated_keys
+ return false;
}
- if (charsWritten > 0)
+ if (result.Length > 0)
{
- traceStateBuffer[charsWritten++] = ',';
+ result.Append(',');
}
- listMember.CopyTo(traceStateBuffer.Slice(charsWritten));
- charsWritten += listMember.Length;
-
- keys++;
+#if NET
+ result.Append(listMember);
+#else
+ result.Append(listMember.ToString());
+#endif
}
}
- tracestateResult = traceStateBuffer.Slice(0, charsWritten).ToString();
-
- return true;
- }
- finally
- {
- if (rentedArray is not null)
- {
- ArrayPool.Shared.Return(rentedArray);
- rentedArray = null;
- }
+ tracestateResult = result.ToString();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static void GrowBuffer(ref char[]? array, ref Span buffer)
- {
- var newBuffer = ArrayPool.Shared.Rent(buffer.Length * 2);
-
- buffer.CopyTo(newBuffer.AsSpan());
-
- if (array is not null)
- {
- ArrayPool.Shared.Return(array);
- }
-
- array = newBuffer;
- buffer = array.AsSpan();
- }
+ return true;
}
private static byte HexCharToByte(char c)
diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceStateUtilsNew.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceStateUtils.cs
similarity index 96%
rename from src/OpenTelemetry.Api/Context/Propagation/TraceStateUtilsNew.cs
rename to src/OpenTelemetry.Api/Context/Propagation/TraceStateUtils.cs
index eef28f8ad97..d81291b5052 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/TraceStateUtilsNew.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/TraceStateUtils.cs
@@ -10,7 +10,7 @@ namespace OpenTelemetry.Context.Propagation;
///
/// Extension methods to extract TraceState from string.
///
-internal static class TraceStateUtilsNew
+internal static class TraceStateUtils
{
private const int KeyMaxSize = 256;
private const int ValueMaxSize = 256;
@@ -98,6 +98,7 @@ internal static bool AppendTraceState(string traceStateString, List>? traceState)
{
+#pragma warning disable CA1851 // Possible multiple enumerations of 'IEnumerable' collection
if (traceState == null || !traceState.Any())
{
return string.Empty;
@@ -125,6 +126,7 @@ internal static string GetString(IEnumerable>? trac
.Append(',');
}
}
+#pragma warning restore CA1851 // Possible multiple enumerations of 'IEnumerable' collection
return sb.Remove(sb.Length - 1, 1).ToString();
}
@@ -153,7 +155,7 @@ private static bool TryParseKeyValue(ReadOnlySpan pair, out ReadOnlySpan
/// The value retrieved from the context slot.
+#pragma warning disable CA1716 // Identifiers should not match keywords
public abstract T? Get();
+#pragma warning restore CA1716 // Identifiers should not match keywords
///
/// Set the value to the context slot.
///
/// The value to be set.
+#pragma warning disable CA1716 // Identifiers should not match keywords
public abstract void Set(T value);
+#pragma warning restore CA1716 // Identifiers should not match keywords
///
public void Dispose()
diff --git a/src/OpenTelemetry.Api/Internal/OpenTelemetryApiEventSource.cs b/src/OpenTelemetry.Api/Internal/OpenTelemetryApiEventSource.cs
index 5ac56d362f3..c2e9cf9dfb2 100644
--- a/src/OpenTelemetry.Api/Internal/OpenTelemetryApiEventSource.cs
+++ b/src/OpenTelemetry.Api/Internal/OpenTelemetryApiEventSource.cs
@@ -12,7 +12,7 @@ namespace OpenTelemetry.Internal;
[EventSource(Name = "OpenTelemetry-Api")]
internal sealed class OpenTelemetryApiEventSource : EventSource
{
- public static OpenTelemetryApiEventSource Log = new();
+ public static readonly OpenTelemetryApiEventSource Log = new();
[NonEvent]
public void ActivityContextExtractException(string format, Exception ex)
diff --git a/src/OpenTelemetry.Api/Logs/LogRecordAttributeList.cs b/src/OpenTelemetry.Api/Logs/LogRecordAttributeList.cs
index 5f6568a8a54..96621ee27ed 100644
--- a/src/OpenTelemetry.Api/Logs/LogRecordAttributeList.cs
+++ b/src/OpenTelemetry.Api/Logs/LogRecordAttributeList.cs
@@ -4,7 +4,7 @@
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
-#if NET && EXPOSE_EXPERIMENTAL_FEATURES
+#if EXPOSE_EXPERIMENTAL_FEATURES
using System.Diagnostics.CodeAnalysis;
#endif
using OpenTelemetry.Internal;
@@ -17,9 +17,7 @@ namespace OpenTelemetry.Logs;
/// Stores attributes to be added to a log message.
///
///
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
///
@@ -27,12 +25,14 @@ namespace OpenTelemetry.Logs;
///
internal
#endif
+#pragma warning disable CA1815 // Override equals and operator equals on value types
struct LogRecordAttributeList : IReadOnlyList>
+#pragma warning restore CA1815 // Override equals and operator equals on value types
{
internal const int OverflowMaxCount = 8;
internal const int OverflowAdditionalCapacity = 16;
internal List>? OverflowAttributes;
- private static readonly IReadOnlyList> Empty = Array.Empty>();
+ private static readonly IReadOnlyList> Empty = [];
private KeyValuePair attribute1;
private KeyValuePair attribute2;
private KeyValuePair attribute3;
@@ -113,7 +113,9 @@ readonly get
/// Attribute name.
/// Attribute value.
[EditorBrowsable(EditorBrowsableState.Never)]
+#pragma warning disable CA1044 // Properties should not be write only
public object? this[string key]
+#pragma warning restore CA1044 // Properties should not be write only
{
// Note: This only exists to enable collection initializer syntax
// like { ["key"] = value }.
@@ -130,7 +132,7 @@ public static LogRecordAttributeList CreateFromEnumerable(IEnumerable> Export(ref List>? attributeStorage)
{
- int count = this.count;
- if (count <= 0)
+ int readonlyCount = this.count;
+ if (readonlyCount <= 0)
{
return Empty;
}
@@ -223,49 +225,49 @@ public readonly Enumerator GetEnumerator()
return overflowAttributes;
}
- Debug.Assert(count <= 8, "Invalid size detected.");
+ Debug.Assert(readonlyCount <= 8, "Invalid size detected.");
attributeStorage ??= new List>(OverflowAdditionalCapacity);
// TODO: Perf test this, adjust as needed.
attributeStorage.Add(this.attribute1);
- if (count == 1)
+ if (readonlyCount == 1)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute2);
- if (count == 2)
+ if (readonlyCount == 2)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute3);
- if (count == 3)
+ if (readonlyCount == 3)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute4);
- if (count == 4)
+ if (readonlyCount == 4)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute5);
- if (count == 5)
+ if (readonlyCount == 5)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute6);
- if (count == 6)
+ if (readonlyCount == 6)
{
return attributeStorage;
}
attributeStorage.Add(this.attribute7);
- if (count == 7)
+ if (readonlyCount == 7)
{
return attributeStorage;
}
diff --git a/src/OpenTelemetry.Api/Logs/LogRecordData.cs b/src/OpenTelemetry.Api/Logs/LogRecordData.cs
index 20d8d5f264c..374b95d736a 100644
--- a/src/OpenTelemetry.Api/Logs/LogRecordData.cs
+++ b/src/OpenTelemetry.Api/Logs/LogRecordData.cs
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
using System.Diagnostics;
-#if NET && EXPOSE_EXPERIMENTAL_FEATURES
+#if EXPOSE_EXPERIMENTAL_FEATURES
using System.Diagnostics.CodeAnalysis;
using OpenTelemetry.Internal;
#endif
@@ -14,9 +14,7 @@ namespace OpenTelemetry.Logs;
/// Stores details about a log message.
///
///
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
///
@@ -24,7 +22,9 @@ namespace OpenTelemetry.Logs;
///
internal
#endif
+#pragma warning disable CA1815 // Override equals and operator equals on value types
struct LogRecordData
+#pragma warning restore CA1815 // Override equals and operator equals on value types
{
internal DateTime TimestampBacking = DateTime.UtcNow;
@@ -123,6 +123,11 @@ public DateTime Timestamp
///
public string? Body { get; set; } = null;
+ ///
+ /// Gets or sets the name of the event associated with the log.
+ ///
+ public string? EventName { get; set; } = null;
+
internal static void SetActivityContext(ref LogRecordData data, Activity? activity)
{
if (activity != null)
diff --git a/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs b/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs
index b6a5b3e000a..73d98881d0a 100644
--- a/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs
+++ b/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-#if NET && EXPOSE_EXPERIMENTAL_FEATURES
+#if EXPOSE_EXPERIMENTAL_FEATURES
using System.Diagnostics.CodeAnalysis;
using OpenTelemetry.Internal;
#endif
@@ -13,9 +13,7 @@ namespace OpenTelemetry.Logs;
/// Describes the severity level of a log record.
///
///
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
///
diff --git a/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs b/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs
index 81453d0302f..14e6e846cdd 100644
--- a/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs
+++ b/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-#if NET && EXPOSE_EXPERIMENTAL_FEATURES
+#if EXPOSE_EXPERIMENTAL_FEATURES
using System.Diagnostics.CodeAnalysis;
using OpenTelemetry.Internal;
#endif
@@ -13,9 +13,7 @@ namespace OpenTelemetry.Logs;
/// Contains extension methods for the enum.
///
///
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
///
@@ -57,8 +55,8 @@ static class LogRecordSeverityExtensions
internal const string Fatal3ShortName = FatalShortName + "3";
internal const string Fatal4ShortName = FatalShortName + "4";
- private static readonly string[] LogRecordSeverityShortNames = new string[]
- {
+ private static readonly string[] LogRecordSeverityShortNames =
+ [
UnspecifiedShortName,
TraceShortName,
@@ -89,8 +87,8 @@ static class LogRecordSeverityExtensions
FatalShortName,
Fatal2ShortName,
Fatal3ShortName,
- Fatal4ShortName,
- };
+ Fatal4ShortName
+ ];
///
/// Returns the OpenTelemetry Specification short name for the
/// WARNING: This is an experimental API which might change or be removed in the future. Use at your own risk.
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
///
diff --git a/src/OpenTelemetry.Api/Logs/LoggerProvider.cs b/src/OpenTelemetry.Api/Logs/LoggerProvider.cs
index 71cc40e2325..c01a9ffd923 100644
--- a/src/OpenTelemetry.Api/Logs/LoggerProvider.cs
+++ b/src/OpenTelemetry.Api/Logs/LoggerProvider.cs
@@ -1,10 +1,10 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-#if NETSTANDARD2_1_OR_GREATER || NET
+#if NETSTANDARD2_1_OR_GREATER || NET || EXPOSE_EXPERIMENTAL_FEATURES
using System.Diagnostics.CodeAnalysis;
#endif
-#if EXPOSE_EXPERIMENTAL_FEATURES && NET
+#if EXPOSE_EXPERIMENTAL_FEATURES
using OpenTelemetry.Internal;
#endif
@@ -30,9 +30,7 @@ protected LoggerProvider()
///
///
/// instance.
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
internal
@@ -47,9 +45,7 @@ Logger GetLogger()
///
/// Optional name identifying the instrumentation library.
/// instance.
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
internal
@@ -65,9 +61,7 @@ Logger GetLogger(string? name)
/// Optional name identifying the instrumentation library.
/// Optional version of the instrumentation library.
/// instance.
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
public
#else
internal
@@ -92,9 +86,7 @@ Logger GetLogger(string? name, string? version)
/// Optional name identifying the instrumentation library.
/// .
/// if the logger was created.
-#if NET
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
-#endif
protected
#else
internal
diff --git a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
index 0e6d270b408..a3a2895ee3e 100644
--- a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
+++ b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
@@ -16,8 +16,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/OpenTelemetry.Api/README.md b/src/OpenTelemetry.Api/README.md
index 09978db05a4..fcaea931073 100644
--- a/src/OpenTelemetry.Api/README.md
+++ b/src/OpenTelemetry.Api/README.md
@@ -249,7 +249,7 @@ following sections describes more features.
### Activity creation options
Basic usage example above showed how `StartActivity` method can be used to start
-an `Activity`. The started activity will automatically becomes the `Current`
+an `Activity`. The started activity automatically becomes the `Current`
activity. It is important to note that the `StartActivity` returns `null`, if no
listeners are interested in the activity to be created. This happens when the
final application does not enable OpenTelemetry, or when OpenTelemetry samplers
diff --git a/src/OpenTelemetry.Api/Trace/SpanContext.cs b/src/OpenTelemetry.Api/Trace/SpanContext.cs
index 825ad62bb41..9ebab851122 100644
--- a/src/OpenTelemetry.Api/Trace/SpanContext.cs
+++ b/src/OpenTelemetry.Api/Trace/SpanContext.cs
@@ -34,7 +34,7 @@ public SpanContext(
bool isRemote = false,
IEnumerable>? traceState = null)
{
- this.ActivityContext = new ActivityContext(traceId, spanId, traceFlags, TraceStateUtilsNew.GetString(traceState), isRemote);
+ this.ActivityContext = new ActivityContext(traceId, spanId, traceFlags, TraceStateUtils.GetString(traceState), isRemote);
}
///
@@ -86,11 +86,11 @@ public IEnumerable> TraceState
var traceState = this.ActivityContext.TraceState;
if (string.IsNullOrEmpty(traceState))
{
- return Enumerable.Empty>();
+ return [];
}
var traceStateResult = new List>();
- TraceStateUtilsNew.AppendTraceState(traceState!, traceStateResult);
+ TraceStateUtils.AppendTraceState(traceState!, traceStateResult);
return traceStateResult;
}
}
@@ -99,7 +99,9 @@ public IEnumerable> TraceState
/// Converts a into an .
///
/// source.
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator ActivityContext(SpanContext spanContext)
+#pragma warning restore CA2225 // Operator overloads have named alternates
=> spanContext.ActivityContext;
///
diff --git a/src/OpenTelemetry.Api/Trace/SpanKind.cs b/src/OpenTelemetry.Api/Trace/SpanKind.cs
index 07358f7b0e8..65a6df7af9c 100644
--- a/src/OpenTelemetry.Api/Trace/SpanKind.cs
+++ b/src/OpenTelemetry.Api/Trace/SpanKind.cs
@@ -6,7 +6,9 @@ namespace OpenTelemetry.Trace;
///
/// Span kind.
///
+#pragma warning disable CA1008 // Enums should have zero value
public enum SpanKind
+#pragma warning restore CA1008 // Enums should have zero value
{
///
/// Span kind was not specified.
diff --git a/src/OpenTelemetry.Api/Trace/Status.cs b/src/OpenTelemetry.Api/Trace/Status.cs
index a39ee8dd754..679d663f4ef 100644
--- a/src/OpenTelemetry.Api/Trace/Status.cs
+++ b/src/OpenTelemetry.Api/Trace/Status.cs
@@ -86,7 +86,11 @@ public override int GetHashCode()
unchecked
{
hash = (31 * hash) + this.StatusCode.GetHashCode();
+#if NET
+ hash = (31 * hash) + (this.Description?.GetHashCode(StringComparison.Ordinal) ?? 0);
+#else
hash = (31 * hash) + (this.Description?.GetHashCode() ?? 0);
+#endif
}
return hash;
diff --git a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs
index 47e67c6095a..0b5ce40141f 100644
--- a/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs
+++ b/src/OpenTelemetry.Api/Trace/TelemetrySpan.cs
@@ -45,9 +45,9 @@ public ActivitySpanId ParentSpanId
/// Status to be set.
public void SetStatus(Status value)
{
-#pragma warning disable
+#pragma warning disable CS0618 // Type or member is obsolete
this.Activity.SetStatus(value);
-#pragma warning restore
+#pragma warning restore CS0618 // Type or member is obsolete
}
///
@@ -226,6 +226,31 @@ public TelemetrySpan AddEvent(string name, DateTimeOffset timestamp, SpanAttribu
return this;
}
+ ///
+ /// Adds a link to another span.
+ ///
+ /// Span context to be linked.
+ /// The instance for chaining.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TelemetrySpan AddLink(SpanContext spanContext)
+ {
+ this.AddLinkInternal(spanContext.ActivityContext);
+ return this;
+ }
+
+ ///
+ /// Adds a link to another span.
+ ///
+ /// Span context to be linked.
+ /// Attributes for the link.
+ /// The instance for chaining.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TelemetrySpan AddLink(SpanContext spanContext, SpanAttributes? attributes)
+ {
+ this.AddLinkInternal(spanContext.ActivityContext, attributes?.Attributes);
+ return this;
+ }
+
///
/// End the span.
///
@@ -342,4 +367,12 @@ private void AddEventInternal(string name, DateTimeOffset timestamp = default, A
this.Activity!.AddEvent(new ActivityEvent(name, timestamp, tags));
}
}
+
+ private void AddLinkInternal(ActivityContext context, ActivityTagsCollection? tags = null)
+ {
+ if (this.IsRecording)
+ {
+ this.Activity!.AddLink(new ActivityLink(context, tags));
+ }
+ }
}
diff --git a/src/OpenTelemetry.Api/Trace/TracerProvider.cs b/src/OpenTelemetry.Api/Trace/TracerProvider.cs
index 21197d0abdb..3246b2e4e7b 100644
--- a/src/OpenTelemetry.Api/Trace/TracerProvider.cs
+++ b/src/OpenTelemetry.Api/Trace/TracerProvider.cs
@@ -33,12 +33,29 @@ protected TracerProvider()
/// Name identifying the instrumentation library.
/// Version of the instrumentation library.
/// Tracer instance.
+ // 1.11.1 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public Tracer GetTracer(
#if NET
[AllowNull]
#endif
string name,
- string? version = null)
+ string? version) =>
+ this.GetTracer(name, version, null);
+
+ ///
+ /// Gets a tracer with given name, version and tags.
+ ///
+ /// Name identifying the instrumentation library.
+ /// Version of the instrumentation library.
+ /// Tags associated with the tracer.
+ /// Tracer instance.
+ public Tracer GetTracer(
+#if NET
+ [AllowNull]
+#endif
+ string name,
+ string? version = null,
+ IEnumerable>? tags = null)
{
var tracers = this.Tracers;
if (tracers == null)
@@ -47,7 +64,7 @@ public Tracer GetTracer(
return new(activitySource: null);
}
- var key = new TracerKey(name, version);
+ var key = new TracerKey(name, version, tags);
if (!tracers.TryGetValue(key, out var tracer))
{
@@ -60,12 +77,10 @@ public Tracer GetTracer(
return new(activitySource: null);
}
- tracer = new(new(key.Name, key.Version));
-#if DEBUG
+ tracer = new(new(key.Name, key.Version, key.Tags));
bool result = tracers.TryAdd(key, tracer);
+#if DEBUG
System.Diagnostics.Debug.Assert(result, "Write into tracers cache failed");
-#else
- tracers.TryAdd(key, tracer);
#endif
}
}
@@ -78,7 +93,7 @@ protected override void Dispose(bool disposing)
{
if (disposing)
{
- var tracers = Interlocked.CompareExchange(ref this.Tracers, null, this.Tracers);
+ var tracers = Interlocked.Exchange(ref this.Tracers, null);
if (tracers != null)
{
lock (tracers)
@@ -103,11 +118,154 @@ internal readonly record struct TracerKey
{
public readonly string Name;
public readonly string? Version;
+ public readonly KeyValuePair[]? Tags;
- public TracerKey(string? name, string? version)
+ public TracerKey(string? name, string? version, IEnumerable>? tags)
{
this.Name = name ?? string.Empty;
this.Version = version;
+ this.Tags = GetOrderedTags(tags);
+ }
+
+ public bool Equals(TracerKey other)
+ {
+ if (!string.Equals(this.Name, other.Name, StringComparison.Ordinal) ||
+ !string.Equals(this.Version, other.Version, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ return AreTagsEqual(this.Tags, other.Tags);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hash = 17;
+#if NET
+ hash = (hash * 31) + (this.Name?.GetHashCode(StringComparison.Ordinal) ?? 0);
+ hash = (hash * 31) + (this.Version?.GetHashCode(StringComparison.Ordinal) ?? 0);
+#else
+ hash = (hash * 31) + (this.Name?.GetHashCode() ?? 0);
+ hash = (hash * 31) + (this.Version?.GetHashCode() ?? 0);
+#endif
+
+ hash = (hash * 31) + GetTagsHashCode(this.Tags);
+ return hash;
+ }
+ }
+
+ private static bool AreTagsEqual(
+ KeyValuePair[]? tags1,
+ KeyValuePair[]? tags2)
+ {
+ if (tags1 == null && tags2 == null)
+ {
+ return true;
+ }
+
+ if (tags1 == null || tags2 == null || tags1.Length != tags2.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < tags1.Length; i++)
+ {
+ var kvp1 = tags1[i];
+ var kvp2 = tags2[i];
+
+ if (!string.Equals(kvp1.Key, kvp2.Key, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ // Compare values
+ if (kvp1.Value is null)
+ {
+ if (kvp2.Value is not null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!kvp1.Value.Equals(kvp2.Value))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static int GetTagsHashCode(
+ IEnumerable>? tags)
+ {
+ if (tags is null)
+ {
+ return 0;
+ }
+
+ var hash = 0;
+ unchecked
+ {
+ foreach (var kvp in tags)
+ {
+#if NET
+ hash = (hash * 31) + kvp.Key.GetHashCode(StringComparison.Ordinal);
+#else
+ hash = (hash * 31) + kvp.Key.GetHashCode();
+#endif
+ if (kvp.Value != null)
+ {
+ hash = (hash * 31) + kvp.Value.GetHashCode()!;
+ }
+ }
+ }
+
+ return hash;
+ }
+
+ private static KeyValuePair[]? GetOrderedTags(
+ IEnumerable>? tags)
+ {
+ if (tags is null)
+ {
+ return null;
+ }
+
+ var orderedTagList = new List>(tags);
+ orderedTagList.Sort((left, right) =>
+ {
+ // First compare by key
+ int keyComparison = string.Compare(left.Key, right.Key, StringComparison.Ordinal);
+ if (keyComparison != 0)
+ {
+ return keyComparison;
+ }
+
+ // If keys are equal, compare by value
+ if (left.Value == null && right.Value == null)
+ {
+ return 0;
+ }
+
+ if (left.Value == null)
+ {
+ return -1;
+ }
+
+ if (right.Value == null)
+ {
+ return 1;
+ }
+
+ // Both values are non-null, compare as strings
+ return string.Compare(left.Value.ToString(), right.Value.ToString(), StringComparison.Ordinal);
+ });
+ return [.. orderedTagList];
}
}
}
diff --git a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md
index 8bf439e1846..8e2ee12d281 100644
--- a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md
@@ -6,6 +6,18 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs b/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs
index 498aa3a926f..d8d04913496 100644
--- a/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs
@@ -32,7 +32,9 @@ public static OpenTelemetryLoggerOptions AddConsoleExporter(this OpenTelemetryLo
var options = new ConsoleExporterOptions();
configure?.Invoke(options);
+#pragma warning disable CA2000 // Dispose objects before losing scope
return loggerOptions.AddProcessor(new SimpleLogRecordExportProcessor(new ConsoleLogRecordExporter(options)));
+#pragma warning restore CA2000 // Dispose objects before losing scope
}
///
diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleExporterMetricsExtensions.cs b/src/OpenTelemetry.Exporter.Console/ConsoleExporterMetricsExtensions.cs
index 78ee90829d9..3d9bf9560f5 100644
--- a/src/OpenTelemetry.Exporter.Console/ConsoleExporterMetricsExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Console/ConsoleExporterMetricsExtensions.cs
@@ -104,11 +104,13 @@ public static MeterProviderBuilder AddConsoleExporter(
});
}
- private static MetricReader BuildConsoleExporterMetricReader(
+ private static PeriodicExportingMetricReader BuildConsoleExporterMetricReader(
ConsoleExporterOptions exporterOptions,
MetricReaderOptions metricReaderOptions)
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
var metricExporter = new ConsoleMetricExporter(exporterOptions);
+#pragma warning restore CA2000 // Dispose objects before losing scope
return PeriodicExportingMetricReaderHelper.CreatePeriodicExportingMetricReader(
metricExporter,
diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
index 0dfe396b30e..84fd449532b 100644
--- a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
+++ b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
@@ -38,7 +38,7 @@ public override ExportResult Export(in Batch batch)
this.WriteLine("The console exporter is still being invoked after it has been disposed. This could be due to the application's incorrect lifecycle management of the LoggerFactory/OpenTelemetry .NET SDK.");
this.WriteLine(Environment.StackTrace);
- this.WriteLine(Environment.NewLine + "Dispose was called on the following stack trace:");
+ this.WriteLine($"{Environment.NewLine}Dispose was called on the following stack trace:");
this.WriteLine(this.disposedStackTrace!);
}
@@ -89,7 +89,7 @@ public override ExportResult Export(in Batch batch)
// Special casing {OriginalFormat}
// See https://github.com/open-telemetry/opentelemetry-dotnet/pull/3182
// for explanation.
- var valueToTransform = logRecord.Attributes[i].Key.Equals("{OriginalFormat}")
+ var valueToTransform = logRecord.Attributes[i].Key.Equals("{OriginalFormat}", StringComparison.Ordinal)
? new KeyValuePair("OriginalFormat (a.k.a Body)", logRecord.Attributes[i].Value)
: logRecord.Attributes[i];
@@ -137,7 +137,7 @@ void ProcessScope(LogRecordScope scope, ConsoleLogRecordExporter exporter)
var resource = this.ParentProvider.GetResource();
if (resource != Resource.Empty)
{
- this.WriteLine("\nResource associated with LogRecord:");
+ this.WriteLine($"{Environment.NewLine}Resource associated with LogRecord:");
foreach (var resourceAttribute in resource.Attributes)
{
if (this.TagWriter.TryTransformTag(resourceAttribute.Key, resourceAttribute.Value, out var result))
diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs b/src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
index 97ba3bbe25c..c47823b3ad6 100644
--- a/src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
+++ b/src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
@@ -17,22 +17,74 @@ public ConsoleMetricExporter(ConsoleExporterOptions options)
public override ExportResult Export(in Batch batch)
{
+ // Print Resource information once at the beginning of the batch
+ var resource = this.ParentProvider.GetResource();
+ if (resource != Resource.Empty)
+ {
+ this.WriteLine("Resource associated with Metrics:");
+ foreach (var resourceAttribute in resource.Attributes)
+ {
+ if (this.TagWriter.TryTransformTag(resourceAttribute.Key, resourceAttribute.Value, out var result))
+ {
+ this.WriteLine($"\t{result.Key}: {result.Value}");
+ }
+ }
+ }
+
foreach (var metric in batch)
{
- var msg = new StringBuilder($"\n");
+ var msg = new StringBuilder(Environment.NewLine);
+#if NET
+ msg.Append(CultureInfo.InvariantCulture, $"Metric Name: {metric.Name}");
+#else
msg.Append($"Metric Name: {metric.Name}");
- if (metric.Description != string.Empty)
+#endif
+ if (!string.IsNullOrEmpty(metric.Description))
{
+#if NET
+ msg.Append(CultureInfo.InvariantCulture, $", Description: {metric.Description}");
+#else
msg.Append($", Description: {metric.Description}");
+#endif
}
- if (metric.Unit != string.Empty)
+ if (!string.IsNullOrEmpty(metric.Unit))
{
+#if NET
+ msg.Append(CultureInfo.InvariantCulture, $", Unit: {metric.Unit}");
+#else
msg.Append($", Unit: {metric.Unit}");
+#endif
}
+#if NET
+ msg.Append(CultureInfo.InvariantCulture, $", Metric Type: {metric.MetricType}");
+#else
+ msg.Append($", Metric Type: {metric.MetricType}");
+#endif
+
this.WriteLine(msg.ToString());
+ // Print Instrumentation scope (Meter) information once per metric
+ this.WriteLine("Instrumentation scope (Meter):");
+ this.WriteLine($"\tName: {metric.MeterName}");
+ if (!string.IsNullOrEmpty(metric.MeterVersion))
+ {
+ this.WriteLine($"\tVersion: {metric.MeterVersion}");
+ }
+
+ if (metric.MeterTags?.Any() == true)
+ {
+ this.WriteLine("\tTags:");
+ foreach (var meterTag in metric.MeterTags)
+ {
+ if (this.TagWriter.TryTransformTag(meterTag, out var result))
+ {
+ this.WriteLine($"\t\t{result.Key}: {result.Value}");
+ }
+ }
+ }
+
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
{
string valueDisplay = string.Empty;
@@ -41,7 +93,11 @@ public override ExportResult Export(in Batch batch)
{
if (this.TagWriter.TryTransformTag(tag, out var result))
{
+#if NET
+ tagsBuilder.Append(CultureInfo.InvariantCulture, $"{result.Key}: {result.Value}");
+#else
tagsBuilder.Append($"{result.Key}: {result.Value}");
+#endif
tagsBuilder.Append(' ');
}
}
@@ -55,10 +111,18 @@ public override ExportResult Export(in Batch batch)
var bucketsBuilder = new StringBuilder();
var sum = metricPoint.GetHistogramSum();
var count = metricPoint.GetHistogramCount();
+#if NET
+ bucketsBuilder.Append(CultureInfo.InvariantCulture, $"Sum: {sum} Count: {count} ");
+#else
bucketsBuilder.Append($"Sum: {sum} Count: {count} ");
+#endif
if (metricPoint.TryGetHistogramMinMaxValues(out double min, out double max))
{
+#if NET
+ bucketsBuilder.Append(CultureInfo.InvariantCulture, $"Min: {min} Max: {max} ");
+#else
bucketsBuilder.Append($"Min: {min} Max: {max} ");
+#endif
}
bucketsBuilder.AppendLine();
@@ -109,7 +173,11 @@ public override ExportResult Export(in Batch batch)
if (exponentialHistogramData.ZeroCount != 0)
{
+#if NET
+ bucketsBuilder.AppendLine(CultureInfo.InvariantCulture, $"Zero Bucket:{exponentialHistogramData.ZeroCount}");
+#else
bucketsBuilder.AppendLine($"Zero Bucket:{exponentialHistogramData.ZeroCount}");
+#endif
}
var offset = exponentialHistogramData.PositiveBuckets.Offset;
@@ -117,7 +185,11 @@ public override ExportResult Export(in Batch batch)
{
var lowerBound = Base2ExponentialBucketHistogramHelper.CalculateLowerBoundary(offset, scale).ToString(CultureInfo.InvariantCulture);
var upperBound = Base2ExponentialBucketHistogramHelper.CalculateLowerBoundary(++offset, scale).ToString(CultureInfo.InvariantCulture);
+#if NET
+ bucketsBuilder.AppendLine(CultureInfo.InvariantCulture, $"({lowerBound}, {upperBound}]:{bucketCount}");
+#else
bucketsBuilder.AppendLine($"({lowerBound}, {upperBound}]:{bucketCount}");
+#endif
}
}
@@ -125,25 +197,11 @@ public override ExportResult Export(in Batch batch)
}
else if (metricType.IsDouble())
{
- if (metricType.IsSum())
- {
- valueDisplay = metricPoint.GetSumDouble().ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- valueDisplay = metricPoint.GetGaugeLastValueDouble().ToString(CultureInfo.InvariantCulture);
- }
+ valueDisplay = metricType.IsSum() ? metricPoint.GetSumDouble().ToString(CultureInfo.InvariantCulture) : metricPoint.GetGaugeLastValueDouble().ToString(CultureInfo.InvariantCulture);
}
else if (metricType.IsLong())
{
- if (metricType.IsSum())
- {
- valueDisplay = metricPoint.GetSumLong().ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- valueDisplay = metricPoint.GetGaugeLastValueLong().ToString(CultureInfo.InvariantCulture);
- }
+ valueDisplay = metricType.IsSum() ? metricPoint.GetSumLong().ToString(CultureInfo.InvariantCulture) : metricPoint.GetGaugeLastValueLong().ToString(CultureInfo.InvariantCulture);
}
var exemplarString = new StringBuilder();
@@ -183,7 +241,11 @@ public override ExportResult Export(in Batch batch)
appendedTagString = true;
}
+#if NET
+ exemplarString.Append(CultureInfo.InvariantCulture, $"{result.Key}: {result.Value}");
+#else
exemplarString.Append($"{result.Key}: {result.Value}");
+#endif
exemplarString.Append(' ');
}
}
@@ -199,55 +261,26 @@ public override ExportResult Export(in Batch batch)
msg.Append(metricPoint.EndTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ", CultureInfo.InvariantCulture));
msg.Append("] ");
msg.Append(tags);
- if (tags != string.Empty)
+ if (string.IsNullOrEmpty(tags))
{
msg.Append(' ');
}
- msg.Append(metric.MetricType);
msg.AppendLine();
+#if NET
+ msg.Append(CultureInfo.InvariantCulture, $"Value: {valueDisplay}");
+#else
msg.Append($"Value: {valueDisplay}");
+#endif
if (exemplarString.Length > 0)
{
msg.AppendLine();
msg.AppendLine("Exemplars");
- msg.Append(exemplarString.ToString());
+ msg.Append(exemplarString);
}
this.WriteLine(msg.ToString());
-
- this.WriteLine("Instrumentation scope (Meter):");
- this.WriteLine($"\tName: {metric.MeterName}");
- if (!string.IsNullOrEmpty(metric.MeterVersion))
- {
- this.WriteLine($"\tVersion: {metric.MeterVersion}");
- }
-
- if (metric.MeterTags?.Any() == true)
- {
- this.WriteLine("\tTags:");
- foreach (var meterTag in metric.MeterTags)
- {
- if (this.TagWriter.TryTransformTag(meterTag, out var result))
- {
- this.WriteLine($"\t\t{result.Key}: {result.Value}");
- }
- }
- }
-
- var resource = this.ParentProvider.GetResource();
- if (resource != Resource.Empty)
- {
- this.WriteLine("Resource associated with Metric:");
- foreach (var resourceAttribute in resource.Attributes)
- {
- if (this.TagWriter.TryTransformTag(resourceAttribute.Key, resourceAttribute.Value, out var result))
- {
- this.WriteLine($"\t{result.Key}: {result.Value}");
- }
- }
- }
}
}
diff --git a/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs b/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs
index fcaf9cdb43c..4950bd100e5 100644
--- a/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs
+++ b/src/OpenTelemetry.Exporter.Console/Implementation/ConsoleTagWriter.cs
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
using System.Diagnostics;
+using System.Globalization;
using System.Text;
using OpenTelemetry.Internal;
@@ -39,13 +40,13 @@ public bool TryTransformTag(string key, object? value, out KeyValuePair
- $(NoWarn),1591
+ $(NoWarn),CS1591
diff --git a/src/OpenTelemetry.Exporter.Console/README.md b/src/OpenTelemetry.Exporter.Console/README.md
index 07a51a29e52..ef7985bf7ef 100644
--- a/src/OpenTelemetry.Exporter.Console/README.md
+++ b/src/OpenTelemetry.Exporter.Console/README.md
@@ -7,8 +7,11 @@ The console exporter prints data to the Console window.
ConsoleExporter supports exporting logs, metrics and traces.
> [!WARNING]
-> This component is intended to be used while learning how telemetry data is
- created and exported. It is not recommended for any production environment.
+> This exporter is intended for debugging and learning purposes. It is not
+ recommended for production use. The output format is not standardized and can
+ change at any time.
+ If a standardized format for exporting telemetry to stdout is desired, upvote on
+ [this feature request](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5920).
## Installation
diff --git a/src/OpenTelemetry.Exporter.InMemory/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.InMemory/AssemblyInfo.cs
deleted file mode 100644
index f7775c1b9b8..00000000000
--- a/src/OpenTelemetry.Exporter.InMemory/AssemblyInfo.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-#if !EXPOSE_EXPERIMENTAL_FEATURES
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)]
-#endif
-
-#if SIGNED
-file static class AssemblyInfo
-{
- public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
-}
-#else
-file static class AssemblyInfo
-{
- public const string PublicKey = "";
-}
-#endif
diff --git a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md
index 6075e746600..4521a32b130 100644
--- a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md
@@ -6,6 +6,18 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterHelperExtensions.cs
index 3940a846bc3..e8af683c695 100644
--- a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterHelperExtensions.cs
+++ b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterHelperExtensions.cs
@@ -20,6 +20,8 @@ public static TracerProviderBuilder AddInMemoryExporter(this TracerProviderBuild
Guard.ThrowIfNull(builder);
Guard.ThrowIfNull(exportedItems);
+#pragma warning disable CA2000 // Dispose objects before losing scope
return builder.AddProcessor(new SimpleActivityExportProcessor(new InMemoryExporter(exportedItems)));
+#pragma warning restore CA2000 // Dispose objects before losing scope
}
}
diff --git a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs
index 19b2079921e..2b665f7a293 100644
--- a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs
+++ b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs
@@ -6,7 +6,9 @@
namespace OpenTelemetry.Logs;
+#pragma warning disable CA1001 // Types that own disposable fields should be disposable - handled by GlobalCleanup
public static class InMemoryExporterLoggingExtensions
+#pragma warning restore CA1001 // Types that own disposable fields should be disposable - handled by GlobalCleanup
{
///
/// Adds InMemory exporter to the OpenTelemetryLoggerOptions.
@@ -22,10 +24,12 @@ public static OpenTelemetryLoggerOptions AddInMemoryExporter(
Guard.ThrowIfNull(loggerOptions);
Guard.ThrowIfNull(exportedItems);
+#pragma warning disable CA2000 // Dispose objects before losing scope
var logExporter = BuildExporter(exportedItems);
return loggerOptions.AddProcessor(
new SimpleLogRecordExportProcessor(logExporter));
+#pragma warning restore CA2000 // Dispose objects before losing scope
}
///
@@ -41,10 +45,12 @@ public static LoggerProviderBuilder AddInMemoryExporter(
Guard.ThrowIfNull(loggerProviderBuilder);
Guard.ThrowIfNull(exportedItems);
+#pragma warning disable CA2000 // Dispose objects before losing scope
var logExporter = BuildExporter(exportedItems);
return loggerProviderBuilder.AddProcessor(
new SimpleLogRecordExportProcessor(logExporter));
+#pragma warning restore CA2000 // Dispose objects before losing scope
}
private static InMemoryExporter BuildExporter(ICollection exportedItems)
diff --git a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs
index da5ecda24d9..a9cc18eb029 100644
--- a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs
+++ b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs
@@ -147,11 +147,13 @@ public static MeterProviderBuilder AddInMemoryExporter(
});
}
- private static MetricReader BuildInMemoryExporterMetricReader(
+ private static PeriodicExportingMetricReader BuildInMemoryExporterMetricReader(
ICollection exportedItems,
MetricReaderOptions metricReaderOptions)
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
var metricExporter = new InMemoryExporter(exportedItems);
+#pragma warning restore CA2000 // Dispose objects before losing scope
return PeriodicExportingMetricReaderHelper.CreatePeriodicExportingMetricReader(
metricExporter,
@@ -160,12 +162,14 @@ private static MetricReader BuildInMemoryExporterMetricReader(
DefaultExportTimeoutMilliseconds);
}
- private static MetricReader BuildInMemoryExporterMetricReader(
+ private static PeriodicExportingMetricReader BuildInMemoryExporterMetricReader(
ICollection exportedItems,
MetricReaderOptions metricReaderOptions)
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
var metricExporter = new InMemoryExporter(
exportFunc: (in Batch metricBatch) => ExportMetricSnapshot(in metricBatch, exportedItems));
+#pragma warning restore CA2000 // Dispose objects before losing scope
return PeriodicExportingMetricReaderHelper.CreatePeriodicExportingMetricReader(
metricExporter,
diff --git a/src/OpenTelemetry.Exporter.InMemory/MetricSnapshot.cs b/src/OpenTelemetry.Exporter.InMemory/MetricSnapshot.cs
index 983cf7bb2b8..fb4bcc5c155 100644
--- a/src/OpenTelemetry.Exporter.InMemory/MetricSnapshot.cs
+++ b/src/OpenTelemetry.Exporter.InMemory/MetricSnapshot.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+using OpenTelemetry.Internal;
+
namespace OpenTelemetry.Metrics;
///
@@ -14,10 +16,11 @@ public class MetricSnapshot
public MetricSnapshot(Metric metric)
{
+ Guard.ThrowIfNull(metric);
this.instrumentIdentity = metric.InstrumentIdentity;
this.MetricType = metric.MetricType;
- List metricPoints = new();
+ List metricPoints = [];
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
{
metricPoints.Add(metricPoint.Copy());
diff --git a/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj b/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj
index 8ca97988f64..5d6f89a7391 100644
--- a/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj
+++ b/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFrameworksForLibraries)
@@ -8,7 +8,7 @@
- $(NoWarn),1591
+ $(NoWarn),CS1591
@@ -19,4 +19,8 @@
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/AssemblyInfo.cs
deleted file mode 100644
index 3f125304deb..00000000000
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/AssemblyInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("MockOpenTelemetryCollector, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests")]
-[assembly: InternalsVisibleTo("Benchmarks")]
-[assembly: InternalsVisibleTo("MockOpenTelemetryCollector")]
-#endif
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs
index e0e999b3e72..baaceb07165 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs
@@ -36,7 +36,9 @@ public static class OpenTelemetryBuilderOtlpExporterExtensions
/// Supplied for chaining calls.
public static IOpenTelemetryBuilder UseOtlpExporter(
this IOpenTelemetryBuilder builder)
+#pragma warning disable CA1062 // Validate arguments of public methods
=> UseOtlpExporter(builder, name: null, configuration: null, configure: null);
+#pragma warning restore CA1062 // Validate arguments of public methods
///
///
@@ -56,7 +58,9 @@ public static IOpenTelemetryBuilder UseOtlpExporter(
{
Guard.ThrowIfNull(baseUrl);
+#pragma warning disable CA1062 // Validate arguments of public methods
return UseOtlpExporter(builder, name: null, configuration: null, configure: otlpBuilder =>
+#pragma warning restore CA1062 // Validate arguments of public methods
{
otlpBuilder.ConfigureDefaultExporterOptions(o =>
{
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
index a3f0013d6ed..79d6d1f3b10 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
@@ -7,13 +7,68 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+* Fixed an issue in .NET Framework where OTLP export of traces, logs, and
+ metrics using `OtlpExportProtocol.Grpc` did not correctly set the initial
+ write position, resulting in gRPC protocol errors.
+ ([#6280](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6280))
+
+* If `EventName` is specified either through `ILogger` or the experimental
+ log bridge API, it is exported as `EventName` by default instead of
+ `logrecord.event.name` which was previously behind the
+ `OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES` feature flag.
+ Note that exporting `logrecord.event.id` is still behind that same feature
+ flag. ([#6306](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6306))
+
+* gRPC calls to export traces, logs, and metrics using `OtlpExportProtocol.Grpc`
+ now set the `TE=trailers` HTTP request header to improve interoperability.
+ ([#6449](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6449))
+
+* Improved performance exporting `byte[]` attributes as native binary format
+ instead of arrays.
+ ([#6534](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6534))
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+* **Breaking Change**: .NET Framework and .NET Standard builds now default to
+ exporting over OTLP/HTTP instead of OTLP/gRPC. **This change could result in a
+ failure to export telemetry unless appropriate measures are taken.**
+ Additionally, if you explicitly configure the exporter to use OTLP/gRPC it may
+ result in a `NotSupportedException` without further configuration. Please
+ carefully review issue
+ ([#6209](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6209))
+ for additional information and workarounds.
+ ([#6229](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6229))
+
+## 1.11.2
+
+Released 2025-Mar-04
+
+* Fixed a bug in .NET Framework gRPC export client where the default success
+ export response was incorrectly marked as false, now changed to true, ensuring
+ exports are correctly marked as successful.
+ ([#6099](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6099))
+
+* Fixed an issues causing trace exports to fail when
+ `Activity.StatusDescription` exceeds 127 bytes.
+ ([#6119](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6119))
+
+* Fixed incorrect log serialization of attributes with null values, causing
+ some backends to reject logs.
+ ([#6149](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6149))
+
## 1.11.1
Released 2025-Jan-22
* Fixed an issue where the OTLP gRPC exporter did not export logs, metrics, or
traces in .NET Framework projects.
- ([#6067](https://github.com/open-telemetry/opentelemetry-dotnet/issues/6067))
+ ([#6083](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6083))
## 1.11.0
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs
index 25b345ac96e..0470b2d8a23 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs
@@ -9,8 +9,6 @@ internal sealed class ExperimentalOptions
{
public const string LogRecordEventIdAttribute = "logrecord.event.id";
- public const string LogRecordEventNameAttribute = "logrecord.event.name";
-
public const string EmitLogEventEnvVar = "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES";
public const string OtlpRetryEnvVar = "OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY";
@@ -29,7 +27,7 @@ public ExperimentalOptions(IConfiguration configuration)
this.EmitLogEventAttributes = emitLogEventAttributes;
}
- if (configuration.TryGetStringValue(OtlpRetryEnvVar, out var retryPolicy) && retryPolicy != null)
+ if (configuration.TryGetStringValue(OtlpRetryEnvVar, out var retryPolicy))
{
if (retryPolicy.Equals("in_memory", StringComparison.OrdinalIgnoreCase))
{
@@ -38,7 +36,7 @@ public ExperimentalOptions(IConfiguration configuration)
else if (retryPolicy.Equals("disk", StringComparison.OrdinalIgnoreCase))
{
this.EnableDiskRetry = true;
- if (configuration.TryGetStringValue(OtlpDiskRetryDirectoryPathEnvVar, out var path) && path != null)
+ if (configuration.TryGetStringValue(OtlpDiskRetryDirectoryPathEnvVar, out var path))
{
this.DiskRetryDirectoryPath = path;
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/.editorconfig b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/.editorconfig
new file mode 100644
index 00000000000..c895abfaeb0
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/.editorconfig
@@ -0,0 +1,2 @@
+[*.cs]
+generated_code = true
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcProtocolHelpers.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcProtocolHelpers.cs
index 29a0a1b4d39..599bfc4a091 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcProtocolHelpers.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcProtocolHelpers.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+#nullable enable
+
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs
index 57efec46b89..3518a0f658d 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+#nullable enable
+
using System.Text;
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
@@ -199,8 +201,8 @@ private static long DecodeVarint(Stream stream)
throw new EndOfStreamException();
}
- result |= (long)(b & 0x7F) << shift;
- if ((b & 0x80) == 0)
+ result |= (long)(b & 0b_0111_1111) << shift;
+ if ((b & 0b_1000_0000) == 0)
{
return result;
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/Status.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/Status.cs
index 89445891970..3dc5c3a7f2e 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/Status.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/Status.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+#nullable enable
+
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/StatusCode.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/StatusCode.cs
index 4c64491c30e..4ab8fefa232 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/StatusCode.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/StatusCode.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+#nullable enable
+
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/TrailingHeadersHelpers.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/TrailingHeadersHelpers.cs
index 6d18a8fe386..a154014d17b 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/TrailingHeadersHelpers.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/TrailingHeadersHelpers.cs
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+#nullable enable
+
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/GrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/GrpcExportClient.cs
deleted file mode 100644
index 6803890f75a..00000000000
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/GrpcExportClient.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-#if NET462_OR_GREATER || NETSTANDARD2_0
-using Grpc.Core;
-using OpenTelemetry.Internal;
-
-using InternalStatus = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc.Status;
-using InternalStatusCode = OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc.StatusCode;
-using Status = Grpc.Core.Status;
-using StatusCode = Grpc.Core.StatusCode;
-
-namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
-
-internal sealed class GrpcExportClient : IExportClient
-{
- private static readonly ExportClientGrpcResponse SuccessExportResponse = new(
- success: false,
- deadlineUtc: default,
- exception: null,
- status: null,
- grpcStatusDetailsHeader: null);
-
- private static readonly Marshaller ByteArrayMarshaller = Marshallers.Create(
- serializer: static input => input,
- deserializer: static data => data);
-
- private readonly Method exportMethod;
-
- private readonly CallInvoker callInvoker;
-
- public GrpcExportClient(OtlpExporterOptions options, string signalPath)
- {
- Guard.ThrowIfNull(options);
- Guard.ThrowIfInvalidTimeout(options.TimeoutMilliseconds);
- Guard.ThrowIfNull(signalPath);
-
- var exporterEndpoint = options.Endpoint.AppendPathIfNotPresent(signalPath);
- this.Endpoint = new UriBuilder(exporterEndpoint).Uri;
- this.Channel = options.CreateChannel();
- this.Headers = options.GetMetadataFromHeaders();
-
- var serviceAndMethod = signalPath.Split('/');
- this.exportMethod = new Method(MethodType.Unary, serviceAndMethod[0], serviceAndMethod[1], ByteArrayMarshaller, ByteArrayMarshaller);
- this.callInvoker = this.Channel.CreateCallInvoker();
- }
-
- internal Channel Channel { get; }
-
- internal Uri Endpoint { get; }
-
- internal Metadata Headers { get; }
-
- public ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default)
- {
- try
- {
- var contentSpan = buffer.AsSpan(0, contentLength);
- this.callInvoker?.BlockingUnaryCall(this.exportMethod, null, new CallOptions(this.Headers, deadlineUtc, cancellationToken), contentSpan.ToArray());
- return SuccessExportResponse;
- }
- catch (RpcException rpcException)
- {
- OpenTelemetryProtocolExporterEventSource.Log.FailedToReachCollector(this.Endpoint, rpcException);
- return new ExportClientGrpcResponse(success: false, deadlineUtc: deadlineUtc, exception: rpcException, ConvertGrpcStatusToStatus(rpcException.Status), rpcException.Trailers.ToString());
- }
- }
-
- public bool Shutdown(int timeoutMilliseconds)
- {
- if (this.Channel == null)
- {
- return true;
- }
-
- if (timeoutMilliseconds == -1)
- {
- this.Channel.ShutdownAsync().Wait();
- return true;
- }
- else
- {
- return Task.WaitAny([this.Channel.ShutdownAsync(), Task.Delay(timeoutMilliseconds)]) == 0;
- }
- }
-
- private static InternalStatus ConvertGrpcStatusToStatus(Status grpcStatus) => grpcStatus.StatusCode switch
- {
- StatusCode.OK => new InternalStatus(InternalStatusCode.OK, grpcStatus.Detail),
- StatusCode.Cancelled => new InternalStatus(InternalStatusCode.Cancelled, grpcStatus.Detail),
- StatusCode.Unknown => new InternalStatus(InternalStatusCode.Unknown, grpcStatus.Detail),
- StatusCode.InvalidArgument => new InternalStatus(InternalStatusCode.InvalidArgument, grpcStatus.Detail),
- StatusCode.DeadlineExceeded => new InternalStatus(InternalStatusCode.DeadlineExceeded, grpcStatus.Detail),
- StatusCode.NotFound => new InternalStatus(InternalStatusCode.NotFound, grpcStatus.Detail),
- StatusCode.AlreadyExists => new InternalStatus(InternalStatusCode.AlreadyExists, grpcStatus.Detail),
- StatusCode.PermissionDenied => new InternalStatus(InternalStatusCode.PermissionDenied, grpcStatus.Detail),
- StatusCode.Unauthenticated => new InternalStatus(InternalStatusCode.Unauthenticated, grpcStatus.Detail),
- StatusCode.ResourceExhausted => new InternalStatus(InternalStatusCode.ResourceExhausted, grpcStatus.Detail),
- StatusCode.FailedPrecondition => new InternalStatus(InternalStatusCode.FailedPrecondition, grpcStatus.Detail),
- StatusCode.Aborted => new InternalStatus(InternalStatusCode.Aborted, grpcStatus.Detail),
- StatusCode.OutOfRange => new InternalStatus(InternalStatusCode.OutOfRange, grpcStatus.Detail),
- StatusCode.Unimplemented => new InternalStatus(InternalStatusCode.Unimplemented, grpcStatus.Detail),
- StatusCode.Internal => new InternalStatus(InternalStatusCode.Internal, grpcStatus.Detail),
- StatusCode.Unavailable => new InternalStatus(InternalStatusCode.Unavailable, grpcStatus.Detail),
- StatusCode.DataLoss => new InternalStatus(InternalStatusCode.DataLoss, grpcStatus.Detail),
- _ => new InternalStatus(InternalStatusCode.Unknown, grpcStatus.Detail),
- };
-}
-#endif
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs
index 7347c69b271..24fc1551cc9 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs
@@ -14,19 +14,12 @@ internal abstract class OtlpExportClient : IExportClient
private static readonly Version Http2RequestVersion = new(2, 0);
#if NET
- private static readonly bool SynchronousSendSupportedByCurrentPlatform;
-
- static OtlpExportClient()
- {
-#if NET
- // See: https://github.com/dotnet/runtime/blob/280f2a0c60ce0378b8db49adc0eecc463d00fe5d/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs#L767
- SynchronousSendSupportedByCurrentPlatform = !OperatingSystem.IsAndroid()
+ // See: https://github.com/dotnet/runtime/blob/280f2a0c60ce0378b8db49adc0eecc463d00fe5d/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs#L767
+ private static readonly bool SynchronousSendSupportedByCurrentPlatform = !OperatingSystem.IsAndroid()
&& !OperatingSystem.IsIOS()
&& !OperatingSystem.IsTvOS()
&& !OperatingSystem.IsBrowser();
#endif
- }
-#endif
protected OtlpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath)
{
@@ -35,7 +28,9 @@ protected OtlpExportClient(OtlpExporterOptions options, HttpClient httpClient, s
Guard.ThrowIfNull(signalPath);
Uri exporterEndpoint;
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
if (options.Protocol == OtlpExportProtocol.Grpc)
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
{
exporterEndpoint = options.Endpoint.AppendPathIfNotPresent(signalPath);
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs
index 1e645c2559f..31b7d0e9bb2 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs
@@ -39,6 +39,11 @@ public override ExportClientResponse SendExportRequest(byte[] buffer, int conten
try
{
using var httpRequest = this.CreateHttpRequest(buffer, contentLength);
+
+ // TE is required by some servers, e.g. C Core.
+ // A missing TE header results in servers aborting the gRPC call.
+ httpRequest.Headers.TryAddWithoutValidation("TE", "trailers");
+
using var httpResponse = this.SendHttpRequest(httpRequest, cancellationToken);
httpResponse.EnsureSuccessStatusCode();
@@ -46,7 +51,7 @@ public override ExportClientResponse SendExportRequest(byte[] buffer, int conten
var trailingHeaders = httpResponse.TrailingHeaders();
Status status = GrpcProtocolHelpers.GetResponseStatus(httpResponse, trailingHeaders);
- if (status.Detail.Equals(Status.NoReplyDetailMessage))
+ if (status.Detail.Equals(Status.NoReplyDetailMessage, StringComparison.Ordinal))
{
#if NET
using var responseStream = httpResponse.Content.ReadAsStream(cancellationToken);
@@ -93,7 +98,7 @@ public override ExportClientResponse SendExportRequest(byte[] buffer, int conten
grpcStatusDetailsHeader = GrpcProtocolHelpers.GetHeaderValue(trailingHeaders, GrpcStatusDetailsHeader);
}
- OpenTelemetryProtocolExporterEventSource.Log.ExportFailure(this.Endpoint.ToString(), "Export failed due to unexpected status code.");
+ OpenTelemetryProtocolExporterEventSource.Log.ExportFailure(this.Endpoint, "Export failed due to unexpected status code.", status);
return new ExportClientGrpcResponse(
success: false,
@@ -158,6 +163,7 @@ private static bool IsTransientNetworkError(HttpRequestException ex)
return ex.InnerException is System.Net.Sockets.SocketException socketEx
&& (socketEx.SocketErrorCode == System.Net.Sockets.SocketError.TimedOut
|| socketEx.SocketErrorCode == System.Net.Sockets.SocketError.ConnectionReset
- || socketEx.SocketErrorCode == System.Net.Sockets.SocketError.HostUnreachable);
+ || socketEx.SocketErrorCode == System.Net.Sockets.SocketError.HostUnreachable
+ || socketEx.SocketErrorCode == System.Net.Sockets.SocketError.ConnectionRefused);
}
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs
index 89afecb4e9d..b4617a1fd42 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs
@@ -3,6 +3,9 @@
using System.Net;
using System.Net.Http.Headers;
+#if NET
+using System.Security.Cryptography;
+#endif
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
@@ -40,13 +43,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClie
///
internal static class OtlpRetry
{
- public const string GrpcStatusDetailsHeader = "grpc-status-details-bin";
public const int InitialBackoffMilliseconds = 1000;
private const int MaxBackoffMilliseconds = 5000;
private const double BackoffMultiplier = 1.5;
#if !NET
- private static readonly Random Random = new Random();
+ private static readonly Random Random = new();
#endif
public static bool TryGetHttpRetryResult(ExportClientHttpResponse response, int retryDelayInMilliSeconds, out RetryResult retryResult)
@@ -156,9 +158,7 @@ private static bool TryGetRetryResult(TStatusCode statusC
return false;
}
- var delayDuration = throttleDelay.HasValue
- ? throttleDelay.Value
- : TimeSpan.FromMilliseconds(GetRandomNumber(0, nextRetryDelayMilliseconds));
+ var delayDuration = throttleDelay ?? TimeSpan.FromMilliseconds(GetRandomNumber(0, nextRetryDelayMilliseconds));
if (deadline.HasValue && IsDeadlineExceeded(deadline + delayDuration))
{
@@ -246,13 +246,15 @@ private static bool IsHttpStatusCodeRetryable(HttpStatusCode statusCode, bool ha
private static int GetRandomNumber(int min, int max)
{
#if NET
- return Random.Shared.Next(min, max);
+ return RandomNumberGenerator.GetInt32(min, max);
#else
// TODO: Implement this better to minimize lock contention.
// Consider pulling in Random.Shared implementation.
lock (Random)
{
+#pragma warning disable CA5394 // Do not use insecure randomness
return Random.Next(min, max);
+#pragma warning restore CA5394 // Do not use insecure randomness
}
#endif
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
index 3596b79fa28..c4d21d92370 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
@@ -3,6 +3,7 @@
using System.Diagnostics.Tracing;
using Microsoft.Extensions.Configuration;
+using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
@@ -94,6 +95,15 @@ public void GrpcRetryDelayParsingFailed(string? grpcStatusDetailsHeader, Excepti
}
}
+ [NonEvent]
+ public void ExportFailure(Uri endpoint, string message, Status status)
+ {
+ if (Log.IsEnabled(EventLevel.Error, EventKeywords.All))
+ {
+ this.ExportFailure(endpoint.ToString(), message, status.ToString());
+ }
+ }
+
[Event(2, Message = "Exporter failed send data to collector to {0} endpoint. Data will not be sent. Exception: {1}", Level = EventLevel.Error)]
public void FailedToReachCollector(string rawCollectorUri, string ex)
{
@@ -208,10 +218,10 @@ public void GrpcStatusWarning(string endpoint, string statusCode)
this.WriteEvent(22, endpoint, statusCode);
}
- [Event(23, Message = "Export failed for {0}. Message: {1}", Level = EventLevel.Error)]
- public void ExportFailure(string endpoint, string message)
+ [Event(23, Message = "Export failed for {0}. Message: {1}. {2}.", Level = EventLevel.Error)]
+ public void ExportFailure(string endpoint, string message, string statusString)
{
- this.WriteEvent(23, endpoint, message);
+ this.WriteEvent(23, endpoint, message, statusString);
}
[Event(24, Message = "Failed to parse gRPC retry delay from header grpcStatusDetailsHeader: '{0}'. Exception: {1}", Level = EventLevel.Warning)]
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs
index f7c97f6e222..c5238b2fa6b 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs
@@ -36,6 +36,7 @@ internal static class ProtobufOtlpLogFieldNumberConstants
internal const int LogRecord_Flags = 8;
internal const int LogRecord_Trace_Id = 9;
internal const int LogRecord_Span_Id = 10;
+ internal const int LogRecord_Event_Name = 12;
// SeverityNumber
internal const int Severity_Number_Unspecified = 0;
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs
index e95ef92812c..c6304b2324e 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs
@@ -56,30 +56,38 @@ internal static int WriteLogsData(ref byte[] buffer, int writePosition, SdkLimit
internal static int TryWriteResourceLogs(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, Dictionary> scopeLogs)
{
- int entryWritePosition = writePosition;
-
- try
+ while (true)
{
- writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpLogFieldNumberConstants.LogsData_Resource_Logs, ProtobufWireType.LEN);
- int logsDataLengthPosition = writePosition;
- writePosition += ReserveSizeForLength;
-
- writePosition = WriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs);
+ int entryWritePosition = writePosition;
- ProtobufSerializer.WriteReservedLength(buffer, logsDataLengthPosition, writePosition - (logsDataLengthPosition + ReserveSizeForLength));
- }
- catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
- {
- writePosition = entryWritePosition;
- if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Logs))
+ try
{
- throw;
+ writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpLogFieldNumberConstants.LogsData_Resource_Logs, ProtobufWireType.LEN);
+ int logsDataLengthPosition = writePosition;
+ writePosition += ReserveSizeForLength;
+
+ writePosition = WriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs);
+
+ ProtobufSerializer.WriteReservedLength(buffer, logsDataLengthPosition, writePosition - (logsDataLengthPosition + ReserveSizeForLength));
+
+ // Serialization succeeded, return the final write position
+ return writePosition;
}
+ catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
+ {
+ // Reset write position and attempt to increase the buffer size
+ writePosition = entryWritePosition;
- return TryWriteResourceLogs(ref buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs);
- }
+ if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Logs))
+ {
+ throw;
+ }
- return writePosition;
+ // Continue the loop to retry serialization with the larger buffer
+ // The loop is limited by the buffer size expansion logic in IncreaseBufferSize,
+ // which stops at a maximum of 100 MB, ensuring this doesn't become an infinite loop
+ }
+ }
}
internal static void ReturnLogRecordListToPool()
@@ -192,11 +200,6 @@ internal static int WriteLogRecord(byte[] buffer, int writePosition, SdkLimitOpt
{
AddLogAttribute(state, ExperimentalOptions.LogRecordEventIdAttribute, logRecord.EventId.Id);
}
-
- if (!string.IsNullOrEmpty(logRecord.EventId.Name))
- {
- AddLogAttribute(state, ExperimentalOptions.LogRecordEventNameAttribute, logRecord.EventId.Name!);
- }
}
if (logRecord.Exception != null)
@@ -223,7 +226,7 @@ internal static int WriteLogRecord(byte[] buffer, int writePosition, SdkLimitOpt
// Special casing {OriginalFormat}
// See https://github.com/open-telemetry/opentelemetry-dotnet/pull/3182
// for explanation.
- if (attribute.Key.Equals("{OriginalFormat}") && !bodyPopulatedFromFormattedMessage)
+ if (attribute.Key.Equals("{OriginalFormat}", StringComparison.Ordinal) && !bodyPopulatedFromFormattedMessage)
{
otlpTagWriterState.WritePosition = WriteLogRecordBody(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, (attribute.Value as string).AsSpan());
isLogRecordBodySet = true;
@@ -254,6 +257,11 @@ internal static int WriteLogRecord(byte[] buffer, int writePosition, SdkLimitOpt
otlpTagWriterState.WritePosition = ProtobufSerializer.WriteFixed32WithTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpLogFieldNumberConstants.LogRecord_Flags, (uint)logRecord.TraceFlags);
}
+ if (logRecord.EventId.Name != null)
+ {
+ otlpTagWriterState.WritePosition = ProtobufSerializer.WriteStringWithTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpLogFieldNumberConstants.LogRecord_Event_Name, logRecord.EventId.Name!);
+ }
+
logRecord.ForEachScope(ProcessScope, state);
if (otlpTagWriterState.DroppedTagCount > 0)
@@ -270,7 +278,7 @@ static void ProcessScope(LogRecordScope scope, SerializationState state)
{
foreach (var scopeItem in scope)
{
- if (scopeItem.Key.Equals("{OriginalFormat}") || string.IsNullOrEmpty(scopeItem.Key))
+ if (scopeItem.Key.Equals("{OriginalFormat}", StringComparison.Ordinal) || string.IsNullOrEmpty(scopeItem.Key))
{
// Ignore if the scope key is empty.
// Ignore if the scope key is {OriginalFormat}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs
index 5064e9cd122..47873bc4c83 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs
@@ -29,7 +29,7 @@ internal static int WriteMetricsData(ref byte[] buffer, int writePosition, Resou
var metricName = metric.MeterName;
if (!scopeMetricsList.TryGetValue(metricName, out var metrics))
{
- metrics = metricListPool.Count > 0 ? metricListPool.Pop() : new List();
+ metrics = metricListPool.Count > 0 ? metricListPool.Pop() : [];
scopeMetricsList[metricName] = metrics;
}
@@ -44,30 +44,34 @@ internal static int WriteMetricsData(ref byte[] buffer, int writePosition, Resou
internal static int TryWriteResourceMetrics(ref byte[] buffer, int writePosition, Resources.Resource? resource, Dictionary> scopeMetrics)
{
- int entryWritePosition = writePosition;
-
- try
+ while (true)
{
- writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.MetricsData_Resource_Metrics, ProtobufWireType.LEN);
- int mericsDataLengthPosition = writePosition;
- writePosition += ReserveSizeForLength;
-
- writePosition = WriteResourceMetrics(buffer, writePosition, resource, scopeMetrics);
+ int entryWritePosition = writePosition;
- ProtobufSerializer.WriteReservedLength(buffer, mericsDataLengthPosition, writePosition - (mericsDataLengthPosition + ReserveSizeForLength));
- }
- catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
- {
- writePosition = entryWritePosition;
- if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Metrics))
+ try
{
- throw;
+ writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.MetricsData_Resource_Metrics, ProtobufWireType.LEN);
+ int mericsDataLengthPosition = writePosition;
+ writePosition += ReserveSizeForLength;
+
+ writePosition = WriteResourceMetrics(buffer, writePosition, resource, scopeMetrics);
+
+ ProtobufSerializer.WriteReservedLength(buffer, mericsDataLengthPosition, writePosition - (mericsDataLengthPosition + ReserveSizeForLength));
+
+ // Serialization succeeded, return the final write position
+ return writePosition;
}
+ catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
+ {
+ // Reset write position and attempt to increase the buffer size
+ writePosition = entryWritePosition;
- return TryWriteResourceMetrics(ref buffer, writePosition, resource, scopeMetrics);
+ if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Metrics))
+ {
+ throw;
+ }
+ }
}
-
- return writePosition;
}
private static void ReturnMetricListToPool()
@@ -117,7 +121,7 @@ private static int WriteScopeMetric(byte[] buffer, int writePosition, string met
int instrumentationScopeLengthPosition = writePosition;
writePosition += ReserveSizeForLength;
- Debug.Assert(metrics.Any(), "Metrics collection is not expected to be empty.");
+ Debug.Assert(metrics.Count > 0, "Metrics collection is not expected to be empty.");
var meterVersion = metrics[0].MeterVersion;
var meterTags = metrics[0].MeterTags;
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs
index 1002f90e7c7..04e60fa0db7 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs
@@ -81,6 +81,27 @@ protected override void OnUnsupportedTagDropped(
tagValueTypeFullName,
tagKey);
+ protected override bool TryWriteEmptyTag(ref OtlpTagWriterState state, string key, object? value)
+ {
+ state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key);
+ state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN);
+ return true;
+ }
+
+ protected override bool TryWriteByteArrayTag(ref OtlpTagWriterState state, string key, ReadOnlySpan value)
+ {
+ // Write KeyValue tag
+ state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key);
+
+ var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)value.Length);
+
+ // length = value.Length + tagSize + length field size.
+ state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.Length + 1 + serializedLengthSize, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN);
+ state.WritePosition = ProtobufSerializer.WriteByteArrayWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Bytes_Value, value);
+
+ return true;
+ }
+
internal struct OtlpTagWriterState
{
public byte[] Buffer;
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs
index a8e0609f8d0..7fb7eb2b5ad 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs
@@ -45,36 +45,38 @@ internal static int WriteTraceData(ref byte[] buffer, int writePosition, SdkLimi
internal static int TryWriteResourceSpans(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource)
{
- int entryWritePosition = writePosition;
-
- try
+ while (true)
{
- writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.TracesData_Resource_Spans, ProtobufWireType.LEN);
- int resourceSpansScopeSpansLengthPosition = writePosition;
- writePosition += ReserveSizeForLength;
+ int entryWritePosition = writePosition;
- writePosition = WriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource);
+ try
+ {
+ writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.TracesData_Resource_Spans, ProtobufWireType.LEN);
+ int resourceSpansScopeSpansLengthPosition = writePosition;
+ writePosition += ReserveSizeForLength;
- ProtobufSerializer.WriteReservedLength(buffer, resourceSpansScopeSpansLengthPosition, writePosition - (resourceSpansScopeSpansLengthPosition + ReserveSizeForLength));
- }
- catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
- {
- writePosition = entryWritePosition;
+ writePosition = WriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource);
- // Attempt to increase the buffer size
- if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Traces))
- {
- throw;
+ ProtobufSerializer.WriteReservedLength(buffer, resourceSpansScopeSpansLengthPosition, writePosition - (resourceSpansScopeSpansLengthPosition + ReserveSizeForLength));
+
+ // Serialization succeeded, return the final write position
+ return writePosition;
}
+ catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
+ {
+ // Reset write position and attempt to increase the buffer size
+ writePosition = entryWritePosition;
- // Retry serialization after increasing the buffer size.
- // The recursion depth is limited to a maximum of 7 calls, as the buffer size starts at ~732 KB
- // and doubles until it reaches the maximum size of 100 MB. This ensures the recursion remains safe
- // and avoids stack overflow.
- return TryWriteResourceSpans(ref buffer, writePosition, sdkLimitOptions, resource);
- }
+ if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Traces))
+ {
+ throw;
+ }
- return writePosition;
+ // Continue the loop to retry serialization with the larger buffer
+ // The loop is limited by the buffer size expansion logic in IncreaseBufferSize,
+ // which stops at a maximum of 100 MB, ensuring this doesn't become an infinite loop
+ }
+ }
}
internal static void ReturnActivityListToPool()
@@ -507,7 +509,10 @@ internal static int WriteSpanStatus(byte[] buffer, int position, Activity activi
{
var descriptionSpan = description.AsSpan();
var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(descriptionSpan);
- position = ProtobufSerializer.WriteTagAndLength(buffer, position, numberOfUtf8CharsInString + 4, ProtobufOtlpTraceFieldNumberConstants.Span_Status, ProtobufWireType.LEN);
+ var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString);
+
+ // length = numberOfUtf8CharsInString + Status_Message tag size + serializedLengthSize field size + Span_Status tag size + Span_Status length size.
+ position = ProtobufSerializer.WriteTagAndLength(buffer, position, numberOfUtf8CharsInString + 1 + serializedLengthSize + 2, ProtobufOtlpTraceFieldNumberConstants.Span_Status, ProtobufWireType.LEN);
position = ProtobufSerializer.WriteStringWithTag(buffer, position, ProtobufOtlpTraceFieldNumberConstants.Status_Message, numberOfUtf8CharsInString, descriptionSpan);
}
else
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs
index a74ec097299..deee79e77db 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs
@@ -18,6 +18,8 @@ internal static class ProtobufSerializer
private const ulong ULong128 = 0x80;
private const int Fixed32Size = 4;
private const int Fixed64Size = 8;
+ private const int MaskBitsLow = 0b_0111_1111;
+ private const int MaskBitHigh = 0b_1000_0000;
private static readonly Encoding Utf8Encoding = Encoding.UTF8;
@@ -42,63 +44,11 @@ internal static int WriteTagAndLength(byte[] buffer, int writePosition, int cont
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void WriteReservedLength(byte[] buffer, int writePosition, int length)
{
- int byteLength = 0;
- int? firstByte = null;
- int? secondByte = null;
- int? thirdByte = null;
- int? fourthByte = null;
-
- do
- {
- switch (byteLength)
- {
- case 0:
- firstByte = length & 0x7F;
- break;
- case 1:
- secondByte = length & 0x7F;
- break;
- case 2:
- thirdByte = length & 0x7F;
- break;
- case 3:
- fourthByte = length & 0x7F;
- break;
- }
-
- length >>= 7;
- byteLength++;
- }
- while (length > 0);
-
- if (fourthByte.HasValue)
- {
- buffer[writePosition++] = (byte)(firstByte!.Value | 0x80);
- buffer[writePosition++] = (byte)(secondByte!.Value | 0x80);
- buffer[writePosition++] = (byte)(thirdByte!.Value | 0x80);
- buffer[writePosition++] = (byte)fourthByte!.Value;
- }
- else if (thirdByte.HasValue)
- {
- buffer[writePosition++] = (byte)(firstByte!.Value | 0x80);
- buffer[writePosition++] = (byte)(secondByte!.Value | 0x80);
- buffer[writePosition++] = (byte)(thirdByte!.Value | 0x80);
- buffer[writePosition++] = 0;
- }
- else if (secondByte.HasValue)
- {
- buffer[writePosition++] = (byte)(firstByte!.Value | 0x80);
- buffer[writePosition++] = (byte)(secondByte!.Value | 0x80);
- buffer[writePosition++] = 0x80;
- buffer[writePosition++] = 0;
- }
- else
- {
- buffer[writePosition++] = (byte)(firstByte!.Value | 0x80);
- buffer[writePosition++] = 0x80;
- buffer[writePosition++] = 0x80;
- buffer[writePosition++] = 0;
- }
+ var slice = buffer.AsSpan(writePosition, 4);
+ slice[0] = (byte)((length & MaskBitsLow) | MaskBitHigh);
+ slice[1] = (byte)(((length >> 7) & MaskBitsLow) | MaskBitHigh);
+ slice[2] = (byte)(((length >> 14) & MaskBitsLow) | MaskBitHigh);
+ slice[3] = (byte)((length >> 21) & MaskBitsLow);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -171,7 +121,7 @@ internal static int WriteVarInt32(byte[] buffer, int writePosition, uint value)
{
while (value >= UInt128)
{
- buffer[writePosition++] = (byte)(0x80 | (value & 0x7F));
+ buffer[writePosition++] = (byte)(MaskBitHigh | (value & MaskBitsLow));
value >>= 7;
}
@@ -184,7 +134,7 @@ internal static int WriteVarInt64(byte[] buffer, int writePosition, ulong value)
{
while (value >= ULong128)
{
- buffer[writePosition++] = (byte)(0x80 | (value & 0x7F));
+ buffer[writePosition++] = (byte)(MaskBitHigh | (value & MaskBitsLow));
value >>= 7;
}
@@ -281,6 +231,17 @@ internal static int ComputeVarInt64Size(ulong value)
return 10;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static int WriteByteArrayWithTag(byte[] buffer, int writePosition, int fieldNumber, ReadOnlySpan value)
+ {
+ writePosition = WriteTag(buffer, writePosition, fieldNumber, ProtobufWireType.LEN);
+ writePosition = WriteLength(buffer, writePosition, value.Length);
+ value.CopyTo(buffer.AsSpan(writePosition));
+
+ writePosition += value.Length;
+ return writePosition;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int WriteStringWithTag(byte[] buffer, int writePosition, int fieldNumber, string value)
{
@@ -325,7 +286,9 @@ internal static int WriteStringWithTag(byte[] buffer, int writePosition, int fie
{
// Note: Validate there is enough space in the buffer to hold the
// string otherwise throw to trigger a resize of the buffer.
+#pragma warning disable CA2201 // Do not raise reserved exception types
throw new IndexOutOfRangeException();
+#pragma warning restore CA2201 // Do not raise reserved exception types
}
unsafe
@@ -340,7 +303,7 @@ internal static int WriteStringWithTag(byte[] buffer, int writePosition, int fie
}
}
#else
- var bytesWritten = Utf8Encoding.GetBytes(value, buffer.AsSpan().Slice(writePosition));
+ var bytesWritten = Utf8Encoding.GetBytes(value, buffer.AsSpan(writePosition));
Debug.Assert(bytesWritten == numberOfUtf8CharsInString, "bytesWritten did not match numberOfUtf8CharsInString");
#endif
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs
index c40d7577908..066ecb02d02 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs
@@ -19,7 +19,9 @@ internal sealed class OtlpExporterPersistentStorageTransmissionHandler : OtlpExp
private bool disposed;
public OtlpExporterPersistentStorageTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds, string storagePath)
+#pragma warning disable CA2000 // Dispose objects before losing scope
: this(new FileBlobProvider(storagePath), exportClient, timeoutMilliseconds)
+#pragma warning restore CA2000 // Dispose objects before losing scope
{
}
@@ -93,6 +95,8 @@ protected override void Dispose(bool disposing)
this.disposed = true;
}
+
+ base.Dispose(disposing);
}
private void RetryStoredRequests()
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj
index a29ba23649e..8470c6df958 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj
@@ -23,10 +23,6 @@
-
-
-
-
@@ -37,4 +33,10 @@
+
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocol.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocol.cs
index 10375e1dfc6..0a01afbd6ef 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocol.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocol.cs
@@ -6,11 +6,16 @@ namespace OpenTelemetry.Exporter;
///
/// Supported by OTLP exporter protocol types according to the specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md.
///
+#pragma warning disable CA1028 // Enum storage should be Int32
public enum OtlpExportProtocol : byte
+#pragma warning restore CA1028 // Enum storage should be Int32
{
///
/// OTLP over gRPC (corresponds to 'grpc' Protocol configuration option). Used as default.
///
+#if NET462_OR_GREATER || NETSTANDARD2_0
+ [Obsolete("CAUTION: OTLP/gRPC is no longer supported for .NET Framework or .NET Standard targets without supplying a properly configured HttpClientFactory. It is strongly encouraged that you migrate to using OTLP/HTTPPROTOBUF.")]
+#endif
Grpc = 0,
///
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocolParser.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocolParser.cs
index e5e788c5617..563c6b8a6b8 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocolParser.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExportProtocolParser.cs
@@ -10,7 +10,9 @@ public static bool TryParse(string value, out OtlpExportProtocol result)
switch (value?.Trim())
{
case "grpc":
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
result = OtlpExportProtocol.Grpc;
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
return true;
case "http/protobuf":
result = OtlpExportProtocol.HttpProtobuf;
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
index 355ab376743..91ebfdbd3e1 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
@@ -26,7 +26,11 @@ public class OtlpExporterOptions : IOtlpExporterOptions
{
internal const string DefaultGrpcEndpoint = "http://localhost:4317";
internal const string DefaultHttpEndpoint = "http://localhost:4318";
+#if NET462_OR_GREATER || NETSTANDARD2_0
+ internal const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.HttpProtobuf;
+#else
internal const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc;
+#endif
internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[]
{
@@ -84,7 +88,9 @@ public Uri Endpoint
{
if (this.endpoint == null)
{
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
return this.Protocol == OtlpExportProtocol.Grpc
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
? new Uri(DefaultGrpcEndpoint)
: new Uri(DefaultHttpEndpoint);
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs
index c4bea6931e3..218b4721caa 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs
@@ -9,9 +9,6 @@
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission;
-#if NET462_OR_GREATER || NETSTANDARD2_0
-using Grpc.Core;
-#endif
namespace OpenTelemetry.Exporter;
@@ -25,30 +22,6 @@ internal static class OtlpExporterOptionsExtensions
private const string MetricsHttpServicePath = "v1/metrics";
private const string LogsHttpServicePath = "v1/logs";
-#if NET462_OR_GREATER || NETSTANDARD2_0
- public static Channel CreateChannel(this OtlpExporterOptions options)
- {
- if (options.Endpoint.Scheme != Uri.UriSchemeHttp && options.Endpoint.Scheme != Uri.UriSchemeHttps)
- {
- throw new NotSupportedException($"Endpoint URI scheme ({options.Endpoint.Scheme}) is not supported. Currently only \"http\" and \"https\" are supported.");
- }
-
- ChannelCredentials channelCredentials;
- if (options.Endpoint.Scheme == Uri.UriSchemeHttps)
- {
- channelCredentials = new SslCredentials();
- }
- else
- {
- channelCredentials = ChannelCredentials.Insecure;
- }
-
- return new Channel(options.Endpoint.Authority, channelCredentials);
- }
-
- public static Metadata GetMetadataFromHeaders(this OtlpExporterOptions options) => options.GetHeaders((m, k, v) => m.Add(k, v));
-#endif
-
public static THeaders GetHeaders(this OtlpExporterOptions options, Action addHeader)
where THeaders : new()
{
@@ -58,23 +31,33 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac
{
// According to the specification, URL-encoded headers must be supported.
optionHeaders = Uri.UnescapeDataString(optionHeaders);
+ ReadOnlySpan headersSpan = optionHeaders.AsSpan();
+
+ while (!headersSpan.IsEmpty)
+ {
+ int commaIndex = headersSpan.IndexOf(',');
+ ReadOnlySpan pair;
+ if (commaIndex == -1)
+ {
+ pair = headersSpan;
+ headersSpan = [];
+ }
+ else
+ {
+ pair = headersSpan.Slice(0, commaIndex);
+ headersSpan = headersSpan.Slice(commaIndex + 1);
+ }
- Array.ForEach(
- optionHeaders.Split(','),
- (pair) =>
+ int equalIndex = pair.IndexOf('=');
+ if (equalIndex == -1)
{
- // Specify the maximum number of substrings to return to 2
- // This treats everything that follows the first `=` in the string as the value to be added for the metadata key
- var keyValueData = pair.Split(new char[] { '=' }, 2);
- if (keyValueData.Length != 2)
- {
- throw new ArgumentException("Headers provided in an invalid format.");
- }
+ throw new ArgumentException("Headers provided in an invalid format.");
+ }
- var key = keyValueData[0].Trim();
- var value = keyValueData[1].Trim();
- addHeader(headers, key, value);
- });
+ var key = pair.Slice(0, equalIndex).Trim().ToString();
+ var value = pair.Slice(equalIndex + 1).Trim().ToString();
+ addHeader(headers, key, value);
+ }
}
foreach (var header in OtlpExporterOptions.StandardHeaders)
@@ -119,25 +102,12 @@ public static IExportClient GetExportClient(this OtlpExporterOptions options, Ot
{
var httpClient = options.HttpClientFactory?.Invoke() ?? throw new InvalidOperationException("OtlpExporterOptions was missing HttpClientFactory or it returned null.");
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
if (options.Protocol != OtlpExportProtocol.Grpc && options.Protocol != OtlpExportProtocol.HttpProtobuf)
{
throw new NotSupportedException($"Protocol {options.Protocol} is not supported.");
}
-#if NET462_OR_GREATER || NETSTANDARD2_0
- if (options.Protocol == OtlpExportProtocol.Grpc)
- {
- var servicePath = otlpSignalType switch
- {
- OtlpSignalType.Traces => TraceGrpcServicePath,
- OtlpSignalType.Metrics => MetricsGrpcServicePath,
- OtlpSignalType.Logs => LogsGrpcServicePath,
- _ => throw new NotSupportedException($"OtlpSignalType {otlpSignalType} is not supported."),
- };
- return new GrpcExportClient(options, servicePath);
- }
-#endif
-
return otlpSignalType switch
{
OtlpSignalType.Traces => options.Protocol == OtlpExportProtocol.Grpc
@@ -154,6 +124,7 @@ public static IExportClient GetExportClient(this OtlpExporterOptions options, Ot
_ => throw new NotSupportedException($"OtlpSignalType {otlpSignalType} is not supported."),
};
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
}
public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptions options, IServiceProvider serviceProvider, string httpClientName)
@@ -174,11 +145,11 @@ public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptio
"CreateClient",
BindingFlags.Public | BindingFlags.Instance,
binder: null,
- new Type[] { typeof(string) },
+ [typeof(string)],
modifiers: null);
if (createClientMethod != null)
{
- HttpClient? client = (HttpClient?)createClientMethod.Invoke(httpClientFactory, new object[] { httpClientName });
+ HttpClient? client = (HttpClient?)createClientMethod.Invoke(httpClientFactory, [httpClientName]);
if (client != null)
{
@@ -200,7 +171,11 @@ internal static Uri AppendPathIfNotPresent(this Uri uri, string path)
var absoluteUri = uri.AbsoluteUri;
var separator = string.Empty;
- if (absoluteUri.EndsWith("/"))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ if (absoluteUri.EndsWith('/'))
+#else
+ if (absoluteUri.EndsWith("/", StringComparison.Ordinal))
+#endif
{
// Endpoint already ends with 'path/'
if (absoluteUri.EndsWith(string.Concat(path, "/"), StringComparison.OrdinalIgnoreCase))
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs
index a87c9ad4d96..dbec08c771b 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs
@@ -58,18 +58,18 @@ internal OtlpLogExporter(
this.experimentalOptions = experimentalOptions!;
this.sdkLimitOptions = sdkLimitOptions!;
-#if NET462_OR_GREATER || NETSTANDARD2_0
- this.startWritePosition = 0;
-#else
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0;
-#endif
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Logs);
}
internal Resource Resource => this.resource ??= this.ParentProvider.GetResource();
///
+#pragma warning disable CA1725 // Parameter names should match base declaration
public override ExportResult Export(in Batch logRecordBatch)
+#pragma warning restore CA1725 // Parameter names should match base declaration
{
// Prevents the exporter's gRPC and HTTP operations from being instrumented.
using var scope = SuppressInstrumentationScope.Begin();
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs
index 42faf425356..851a7729286 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs
@@ -56,7 +56,9 @@ public static OpenTelemetryLoggerOptions AddOtlpExporter(
var finalOptionsName = name ?? Options.DefaultName;
+#pragma warning disable CA1062 // Validate arguments of public methods
return loggerOptions.AddProcessor(sp =>
+#pragma warning restore CA1062 // Validate arguments of public methods
{
var exporterOptions = GetOptions(sp, name, finalOptionsName, OtlpExporterOptions.CreateOtlpExporterOptions);
@@ -102,7 +104,9 @@ public static OpenTelemetryLoggerOptions AddOtlpExporter(
var finalOptionsName = name ?? Options.DefaultName;
+#pragma warning disable CA1062 // Validate arguments of public methods
return loggerOptions.AddProcessor(sp =>
+#pragma warning restore CA1062 // Validate arguments of public methods
{
var exporterOptions = GetOptions(sp, name, finalOptionsName, OtlpExporterOptions.CreateOtlpExporterOptions);
@@ -284,6 +288,17 @@ internal static BaseProcessor BuildOtlpLogExporter(
Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null");
Debug.Assert(experimentalOptions != null, "experimentalOptions was null");
+#if NET462_OR_GREATER || NETSTANDARD2_0
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
+ if (exporterOptions!.Protocol == OtlpExportProtocol.Grpc &&
+ ReferenceEquals(exporterOptions.HttpClientFactory, exporterOptions.DefaultHttpClientFactory))
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
+ {
+ throw new NotSupportedException("OtlpExportProtocol.Grpc with the default HTTP client factory is not supported on .NET Framework or .NET Standard 2.0." +
+ "Please switch to OtlpExportProtocol.HttpProtobuf or provide a custom HttpClientFactory.");
+ }
+#endif
+
if (!skipUseOtlpExporterRegistrationCheck)
{
serviceProvider!.EnsureNoUseOtlpExporterRegistrations();
@@ -305,30 +320,40 @@ internal static BaseProcessor BuildOtlpLogExporter(
* "OtlpLogExporter");
*/
+#pragma warning disable CA2000 // Dispose objects before losing scope
BaseExporter otlpExporter = new OtlpLogExporter(
exporterOptions!,
sdkLimitOptions!,
experimentalOptions!);
+#pragma warning restore CA2000 // Dispose objects before losing scope
- if (configureExporterInstance != null)
+ try
{
- otlpExporter = configureExporterInstance(otlpExporter);
- }
+ if (configureExporterInstance != null)
+ {
+ otlpExporter = configureExporterInstance(otlpExporter);
+ }
- if (processorOptions!.ExportProcessorType == ExportProcessorType.Simple)
- {
- return new SimpleLogRecordExportProcessor(otlpExporter);
+ if (processorOptions!.ExportProcessorType == ExportProcessorType.Simple)
+ {
+ return new SimpleLogRecordExportProcessor(otlpExporter);
+ }
+ else
+ {
+ var batchOptions = processorOptions.BatchExportProcessorOptions;
+
+ return new BatchLogRecordExportProcessor(
+ otlpExporter,
+ batchOptions.MaxQueueSize,
+ batchOptions.ScheduledDelayMilliseconds,
+ batchOptions.ExporterTimeoutMilliseconds,
+ batchOptions.MaxExportBatchSize);
+ }
}
- else
+ catch
{
- var batchOptions = processorOptions.BatchExportProcessorOptions;
-
- return new BatchLogRecordExportProcessor(
- otlpExporter,
- batchOptions.MaxQueueSize,
- batchOptions.ScheduledDelayMilliseconds,
- batchOptions.ExporterTimeoutMilliseconds,
- batchOptions.MaxExportBatchSize);
+ otlpExporter.Dispose();
+ throw;
}
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs
index 8c596e9c61c..1ff7dcc5ae5 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs
@@ -51,18 +51,18 @@ internal OtlpMetricExporter(
Debug.Assert(exporterOptions != null, "exporterOptions was null");
Debug.Assert(experimentalOptions != null, "experimentalOptions was null");
-#if NET462_OR_GREATER || NETSTANDARD2_0
- this.startWritePosition = 0;
-#else
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0;
-#endif
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Metrics);
}
internal Resource Resource => this.resource ??= this.ParentProvider.GetResource();
///
+#pragma warning disable CA1725 // Parameter names should match base declaration
public override ExportResult Export(in Batch metrics)
+#pragma warning restore CA1725 // Parameter names should match base declaration
{
// Prevents the exporter's gRPC and HTTP operations from being instrumented.
using var scope = SuppressInstrumentationScope.Begin();
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
index 1a8ef2f1b42..4efbc2c8b18 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
@@ -168,6 +168,17 @@ internal static MetricReader BuildOtlpExporterMetricReader(
Debug.Assert(metricReaderOptions != null, "metricReaderOptions was null");
Debug.Assert(experimentalOptions != null, "experimentalOptions was null");
+#if NET462_OR_GREATER || NETSTANDARD2_0
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
+ if (exporterOptions!.Protocol == OtlpExportProtocol.Grpc &&
+ ReferenceEquals(exporterOptions.HttpClientFactory, exporterOptions.DefaultHttpClientFactory))
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
+ {
+ throw new NotSupportedException("OtlpExportProtocol.Grpc with the default HTTP client factory is not supported on .NET Framework or .NET Standard 2.0." +
+ "Please switch to OtlpExportProtocol.HttpProtobuf or provide a custom HttpClientFactory.");
+ }
+#endif
+
if (!skipUseOtlpExporterRegistrationCheck)
{
serviceProvider!.EnsureNoUseOtlpExporterRegistrations();
@@ -175,7 +186,9 @@ internal static MetricReader BuildOtlpExporterMetricReader(
exporterOptions!.TryEnableIHttpClientFactoryIntegration(serviceProvider!, "OtlpMetricExporter");
+#pragma warning disable CA2000 // Dispose objects before losing scope
BaseExporter metricExporter = new OtlpMetricExporter(exporterOptions!, experimentalOptions!);
+#pragma warning restore CA2000 // Dispose objects before losing scope
if (configureExporterInstance != null)
{
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs
index d30abeeece0..6f10f51e7b2 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs
@@ -54,18 +54,18 @@ internal OtlpTraceExporter(
Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null");
this.sdkLimitOptions = sdkLimitOptions!;
-#if NET462_OR_GREATER || NETSTANDARD2_0
- this.startWritePosition = 0;
-#else
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0;
-#endif
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions, OtlpSignalType.Traces);
}
internal Resource Resource => this.resource ??= this.ParentProvider.GetResource();
///
+#pragma warning disable CA1725 // Parameter names should match base declaration
public override ExportResult Export(in Batch activityBatch)
+#pragma warning restore CA1725 // Parameter names should match base declaration
{
// Prevents the exporter's gRPC and HTTP operations from being instrumented.
using var scope = SuppressInstrumentationScope.Begin();
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs
index 3a18b3da423..831a1380bb6 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs
@@ -129,6 +129,17 @@ internal static BaseProcessor BuildOtlpExporterProcessor(
Debug.Assert(experimentalOptions != null, "experimentalOptions was null");
Debug.Assert(batchExportProcessorOptions != null, "batchExportProcessorOptions was null");
+#if NET462_OR_GREATER || NETSTANDARD2_0
+#pragma warning disable CS0618 // Suppressing gRPC obsolete warning
+ if (exporterOptions!.Protocol == OtlpExportProtocol.Grpc &&
+ ReferenceEquals(exporterOptions.HttpClientFactory, exporterOptions.DefaultHttpClientFactory))
+#pragma warning restore CS0618 // Suppressing gRPC obsolete warning
+ {
+ throw new NotSupportedException("OtlpExportProtocol.Grpc with the default HTTP client factory is not supported on .NET Framework or .NET Standard 2.0." +
+ "Please switch to OtlpExportProtocol.HttpProtobuf or provide a custom HttpClientFactory.");
+ }
+#endif
+
if (!skipUseOtlpExporterRegistrationCheck)
{
serviceProvider!.EnsureNoUseOtlpExporterRegistrations();
@@ -136,25 +147,35 @@ internal static BaseProcessor BuildOtlpExporterProcessor(
exporterOptions!.TryEnableIHttpClientFactoryIntegration(serviceProvider!, "OtlpTraceExporter");
+#pragma warning disable CA2000 // Dispose objects before losing scope
BaseExporter otlpExporter = new OtlpTraceExporter(exporterOptions!, sdkLimitOptions!, experimentalOptions!);
+#pragma warning restore CA2000 // Dispose objects before losing scope
- if (configureExporterInstance != null)
+ try
{
- otlpExporter = configureExporterInstance(otlpExporter);
- }
+ if (configureExporterInstance != null)
+ {
+ otlpExporter = configureExporterInstance(otlpExporter);
+ }
- if (exportProcessorType == ExportProcessorType.Simple)
- {
- return new SimpleActivityExportProcessor(otlpExporter);
+ if (exportProcessorType == ExportProcessorType.Simple)
+ {
+ return new SimpleActivityExportProcessor(otlpExporter);
+ }
+ else
+ {
+ return new BatchActivityExportProcessor(
+ otlpExporter,
+ batchExportProcessorOptions!.MaxQueueSize,
+ batchExportProcessorOptions.ScheduledDelayMilliseconds,
+ batchExportProcessorOptions.ExporterTimeoutMilliseconds,
+ batchExportProcessorOptions.MaxExportBatchSize);
+ }
}
- else
+ catch
{
- return new BatchActivityExportProcessor(
- otlpExporter,
- batchExportProcessorOptions!.MaxQueueSize,
- batchExportProcessorOptions.ScheduledDelayMilliseconds,
- batchExportProcessorOptions.ExporterTimeoutMilliseconds,
- batchExportProcessorOptions.MaxExportBatchSize);
+ otlpExporter.Dispose();
+ throw;
}
}
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/DirectorySizeTracker.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/DirectorySizeTracker.cs
index 2bbc352817d..d251885f81d 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/DirectorySizeTracker.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/DirectorySizeTracker.cs
@@ -56,16 +56,16 @@ internal static long CalculateFolderSize(string path)
long directorySize = 0;
try
{
- foreach (string file in Directory.EnumerateFiles(path))
+ foreach (var file in Directory.EnumerateFiles(path))
{
if (File.Exists(file))
{
- FileInfo fileInfo = new FileInfo(file);
+ var fileInfo = new FileInfo(file);
directorySize += fileInfo.Length;
}
}
- foreach (string dir in Directory.GetDirectories(path))
+ foreach (var dir in Directory.GetDirectories(path))
{
directorySize += CalculateFolderSize(dir);
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlob.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlob.cs
index cfb1c395d08..72f121b9508 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlob.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlob.cs
@@ -58,7 +58,7 @@ protected override bool OnTryWrite(byte[] buffer, int leasePeriodMilliseconds =
{
Guard.ThrowIfNull(buffer);
- string path = this.FullPath + ".tmp";
+ var path = this.FullPath + ".tmp";
try
{
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlobProvider.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlobProvider.cs
index 4d790469958..9e79fb28736 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlobProvider.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/FileBlobProvider.cs
@@ -5,7 +5,6 @@
using System.Timers;
using OpenTelemetry.Internal;
using OpenTelemetry.PersistentStorage.Abstractions;
-using Timer = System.Timers.Timer;
namespace OpenTelemetry.PersistentStorage.FileSystem;
@@ -24,7 +23,7 @@ public class FileBlobProvider : PersistentBlobProvider, IDisposable
private readonly DirectorySizeTracker directorySizeTracker;
private readonly long retentionPeriodInMilliseconds;
private readonly int writeTimeoutInMilliseconds;
- private readonly Timer maintenanceTimer;
+ private readonly System.Timers.Timer maintenanceTimer;
private bool disposedValue;
///
@@ -61,7 +60,7 @@ public class FileBlobProvider : PersistentBlobProvider, IDisposable
/// path exceeds system defined maximum length.
///
///
- /// insufficient priviledges for provided path.
+ /// insufficient privileges for provided path.
///
///
/// path contains a colon character (:) that is not part of a drive label ("C:\").
@@ -87,7 +86,7 @@ public FileBlobProvider(
this.retentionPeriodInMilliseconds = retentionPeriodInMilliseconds;
this.writeTimeoutInMilliseconds = writeTimeoutInMilliseconds;
- this.maintenanceTimer = new Timer(maintenancePeriodInMilliseconds);
+ this.maintenanceTimer = new System.Timers.Timer(maintenancePeriodInMilliseconds);
this.maintenanceTimer.Elapsed += this.OnMaintenanceEvent;
this.maintenanceTimer.AutoReset = true;
this.maintenanceTimer.Enabled = true;
@@ -105,7 +104,7 @@ protected override IEnumerable OnGetBlobs()
foreach (var file in Directory.EnumerateFiles(this.DirectoryPath, "*.blob", SearchOption.TopDirectoryOnly).OrderByDescending(f => f))
{
- DateTime fileDateTime = PersistentStorageHelper.GetDateTimeFromBlobName(file);
+ var fileDateTime = PersistentStorageHelper.GetDateTimeFromBlobName(file);
if (fileDateTime > retentionDeadline)
{
yield return new FileBlob(file, this.directorySizeTracker);
@@ -174,7 +173,7 @@ private void OnMaintenanceEvent(object? source, ElapsedEventArgs e)
private bool CheckStorageSize()
{
- if (!this.directorySizeTracker.IsSpaceAvailable(out long size))
+ if (!this.directorySizeTracker.IsSpaceAvailable(out var size))
{
// TODO: check accuracy of size reporting.
PersistentStorageEventSource.Log.PersistentStorageWarning(
@@ -186,7 +185,7 @@ private bool CheckStorageSize()
return true;
}
- private PersistentBlob? CreateFileBlob(byte[] buffer, int leasePeriodMilliseconds = 0)
+ private FileBlob? CreateFileBlob(byte[] buffer, int leasePeriodMilliseconds = 0)
{
if (!this.CheckStorageSize())
{
@@ -198,14 +197,7 @@ private bool CheckStorageSize()
var blobFilePath = Path.Combine(this.DirectoryPath, PersistentStorageHelper.GetUniqueFileName(".blob"));
var blob = new FileBlob(blobFilePath, this.directorySizeTracker);
- if (blob.TryWrite(buffer, leasePeriodMilliseconds))
- {
- return blob;
- }
- else
- {
- return null;
- }
+ return blob.TryWrite(buffer, leasePeriodMilliseconds) ? blob : null;
}
catch (Exception ex)
{
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentBlobProvider.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentBlobProvider.cs
index 29d3dee8a02..465af27746d 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentBlobProvider.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentBlobProvider.cs
@@ -102,12 +102,12 @@ public IEnumerable GetBlobs()
{
try
{
- return this.OnGetBlobs() ?? Enumerable.Empty();
+ return this.OnGetBlobs() ?? [];
}
catch (Exception ex)
{
PersistentStorageAbstractionsEventSource.Log.PersistentStorageAbstractionsException(nameof(PersistentBlobProvider), "Failed to get all the blobs", ex);
- return Enumerable.Empty();
+ return [];
}
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentStorageHelper.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentStorageHelper.cs
index 63005646fc7..6d2fd821cbb 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentStorageHelper.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/PersistentStorageHelper.cs
@@ -12,7 +12,7 @@ internal static void RemoveExpiredBlob(DateTime retentionDeadline, string filePa
{
if (filePath.EndsWith(".blob", StringComparison.OrdinalIgnoreCase))
{
- DateTime fileDateTime = GetDateTimeFromBlobName(filePath);
+ var fileDateTime = GetDateTimeFromBlobName(filePath);
if (fileDateTime < retentionDeadline)
{
try
@@ -30,11 +30,11 @@ internal static void RemoveExpiredBlob(DateTime retentionDeadline, string filePa
internal static bool RemoveExpiredLease(DateTime leaseDeadline, string filePath)
{
- bool success = false;
+ var success = false;
if (filePath.EndsWith(".lock", StringComparison.OrdinalIgnoreCase))
{
- DateTime fileDateTime = GetDateTimeFromLeaseName(filePath);
+ var fileDateTime = GetDateTimeFromLeaseName(filePath);
if (fileDateTime < leaseDeadline)
{
var newFilePath = filePath.Substring(0, filePath.LastIndexOf('@'));
@@ -55,11 +55,11 @@ internal static bool RemoveExpiredLease(DateTime leaseDeadline, string filePath)
internal static bool RemoveTimedOutTmpFiles(DateTime timeoutDeadline, string filePath)
{
- bool success = false;
+ var success = false;
if (filePath.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase))
{
- DateTime fileDateTime = GetDateTimeFromBlobName(filePath);
+ var fileDateTime = GetDateTimeFromBlobName(filePath);
if (fileDateTime < timeoutDeadline)
{
try
@@ -144,7 +144,7 @@ internal static DateTime GetDateTimeFromLeaseName(string filePath)
{
var fileName = Path.GetFileNameWithoutExtension(filePath);
var startIndex = fileName.LastIndexOf('@') + 1;
- var time = fileName.Substring(startIndex, fileName.Length - startIndex);
+ var time = fileName.Substring(startIndex);
DateTime.TryParseExact(time, "yyyy-MM-ddTHHmmss.fffffffZ", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTime);
return dateTime.ToUniversalTime();
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/README.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/README.md
index 018aef5b4a8..353cd8dffbc 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/README.md
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/PersistentStorage/README.md
@@ -1,9 +1,9 @@
# Persistent Storage APIs for OTLP Exporter
The files in this folder have been copied over from
-[OpenTelemetry.PersistentStorage.Abstractions](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/58607b7cdeb15207027a6fa4ca56e7fac897bda4/src/OpenTelemetry.PersistentStorage.Abstractions)
+[OpenTelemetry.PersistentStorage.Abstractions](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/1be4157075ad09e13aa54b8e5845d2310bd82673/src/OpenTelemetry.PersistentStorage.Abstractions)
and
-[OpenTelemetry.PersistentStorage.FileSystem](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/58607b7cdeb15207027a6fa4ca56e7fac897bda4/src/OpenTelemetry.PersistentStorage.FileSystem).
+[OpenTelemetry.PersistentStorage.FileSystem](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/1be4157075ad09e13aa54b8e5845d2310bd82673/src/OpenTelemetry.PersistentStorage.FileSystem).
Any code changes in this folder MUST go through changes in the original location
i.e. in the contrib repo. Here is the sequence of steps to be followed when
making changes:
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs
deleted file mode 100644
index 59d05ae59c9..00000000000
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/AssemblyInfo.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests")]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests")]
-#endif
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md
index 07256d56975..768eb691981 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md
@@ -7,6 +7,20 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.12.0-beta.1
+
+Released 2025-May-06
+
+* Updated OpenTelemetry core component version(s) to `1.12.0`.
+ ([#6269](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6269))
+
+## 1.11.2-beta.1
+
+Released 2025-Mar-05
+
+* Updated OpenTelemetry core component version(s) to `1.11.2`.
+ ([#6169](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6169))
+
## 1.11.0-beta.1
Released 2025-Jan-16
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj
index f6a49fe6a64..1979137998b 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj
@@ -36,5 +36,10 @@
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs
index 38559e6b201..92bef6f1b59 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs
@@ -99,6 +99,8 @@ public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint(
Action? configureBranchedPipeline,
string? optionsName)
{
+ Guard.ThrowIfNull(app);
+
// Note: Order is important here. MeterProvider is accessed before
// GetOptions so that any changes made to
// PrometheusAspNetCoreOptions in deferred AddPrometheusExporter
@@ -114,7 +116,7 @@ public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint(
path = options.ScrapeEndpointPath ?? PrometheusAspNetCoreOptions.DefaultScrapeEndpointPath;
}
- if (!path.StartsWith("/"))
+ if (!path.StartsWith('/'))
{
path = $"/{path}";
}
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs
index 45639b378fc..6604935ebf0 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs
@@ -69,6 +69,8 @@ public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(
Action? configureBranchedPipeline,
string? optionsName)
{
+ Guard.ThrowIfNull(endpoints);
+
var builder = endpoints.CreateApplicationBuilder();
// Note: Order is important here. MeterProvider is accessed before
@@ -84,7 +86,7 @@ public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(
path = options.ScrapeEndpointPath ?? PrometheusAspNetCoreOptions.DefaultScrapeEndpointPath;
}
- if (!path.StartsWith("/"))
+ if (!path.StartsWith('/'))
{
path = $"/{path}";
}
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs
index f73455d3b08..e3567d4ad63 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs
@@ -62,9 +62,11 @@ public static MeterProviderBuilder AddPrometheusExporter(
});
}
- private static MetricReader BuildPrometheusExporterMetricReader(PrometheusAspNetCoreOptions options)
+ private static BaseExportingMetricReader BuildPrometheusExporterMetricReader(PrometheusAspNetCoreOptions options)
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
var exporter = new PrometheusExporter(options.ExporterOptions);
+#pragma warning restore CA2000 // Dispose objects before losing scope
return new BaseExportingMetricReader(exporter)
{
diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs
index f91a93f66bb..863695b975f 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs
@@ -74,7 +74,7 @@ public async Task InvokeAsync(HttpContext httpContext)
? "application/openmetrics-text; version=1.0.0; charset=utf-8"
: "text/plain; charset=utf-8; version=0.0.4";
- await response.Body.WriteAsync(dataView.Array!, 0, dataView.Count).ConfigureAwait(false);
+ await response.Body.WriteAsync(dataView.Array.AsMemory(0, dataView.Count)).ConfigureAwait(false);
}
else
{
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs
deleted file mode 100644
index 7d6b4b08289..00000000000
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/AssemblyInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests")]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.Tests")]
-[assembly: InternalsVisibleTo("Benchmarks")]
-#endif
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md
index ad0b8e591d1..7ab44c75ca1 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md
@@ -7,6 +7,20 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.12.0-beta.1
+
+Released 2025-May-06
+
+* Updated OpenTelemetry core component version(s) to `1.12.0`.
+ ([#6269](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6269))
+
+## 1.11.2-beta.1
+
+Released 2025-Mar-05
+
+* Updated OpenTelemetry core component version(s) to `1.11.2`.
+ ([#6169](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6169))
+
## 1.11.0-beta.1
Released 2025-Jan-16
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs
index 1c74e36bcf8..ce4c5384485 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs
@@ -34,8 +34,8 @@ public PrometheusCollectionManager(PrometheusExporter exporter)
this.exporter = exporter;
this.scrapeResponseCacheDurationMilliseconds = this.exporter.ScrapeResponseCacheDurationMilliseconds;
this.onCollectRef = this.OnCollect;
- this.metricsCache = new Dictionary();
- this.scopes = new HashSet();
+ this.metricsCache = [];
+ this.scopes = [];
}
#if NET
@@ -68,10 +68,7 @@ public Task EnterCollect(bool openMetricsRequested)
// If a collection is already running, return a task to wait on the result.
if (this.collectionRunning)
{
- if (this.collectionTcs == null)
- {
- this.collectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- }
+ this.collectionTcs ??= new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
Interlocked.Increment(ref this.readerCount);
this.ExitGlobalLock();
@@ -148,6 +145,22 @@ public void ExitCollect()
Interlocked.Decrement(ref this.readerCount);
}
+ private static bool IncreaseBufferSize(ref byte[] buffer)
+ {
+ var newBufferSize = buffer.Length * 2;
+
+ if (newBufferSize > 100 * 1024 * 1024)
+ {
+ return false;
+ }
+
+ var newBuffer = new byte[newBufferSize];
+ buffer.CopyTo(newBuffer, 0);
+ buffer = newBuffer;
+
+ return true;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnterGlobalLock()
{
@@ -230,7 +243,7 @@ private ExportResult OnCollect(in Batch metrics)
}
catch (IndexOutOfRangeException)
{
- if (!this.IncreaseBufferSize(ref buffer))
+ if (!IncreaseBufferSize(ref buffer))
{
// there are two cases we might run into the following condition:
// 1. we have many metrics to be exported - in this case we probably want
@@ -268,7 +281,7 @@ private ExportResult OnCollect(in Batch metrics)
}
catch (IndexOutOfRangeException)
{
- if (!this.IncreaseBufferSize(ref buffer))
+ if (!IncreaseBufferSize(ref buffer))
{
throw;
}
@@ -285,7 +298,7 @@ private ExportResult OnCollect(in Batch metrics)
}
catch (IndexOutOfRangeException)
{
- if (!this.IncreaseBufferSize(ref buffer))
+ if (!IncreaseBufferSize(ref buffer))
{
throw;
}
@@ -307,11 +320,11 @@ private ExportResult OnCollect(in Batch metrics)
{
if (this.exporter.OpenMetricsRequested)
{
- this.previousOpenMetricsDataView = new ArraySegment(Array.Empty(), 0, 0);
+ this.previousOpenMetricsDataView = new ArraySegment([], 0, 0);
}
else
{
- this.previousPlainTextDataView = new ArraySegment(Array.Empty(), 0, 0);
+ this.previousPlainTextDataView = new ArraySegment([], 0, 0);
}
return ExportResult.Failure;
@@ -332,7 +345,7 @@ private int WriteTargetInfo(ref byte[] buffer)
}
catch (IndexOutOfRangeException)
{
- if (!this.IncreaseBufferSize(ref buffer))
+ if (!IncreaseBufferSize(ref buffer))
{
throw;
}
@@ -343,22 +356,6 @@ private int WriteTargetInfo(ref byte[] buffer)
return this.targetInfoBufferLength;
}
- private bool IncreaseBufferSize(ref byte[] buffer)
- {
- var newBufferSize = buffer.Length * 2;
-
- if (newBufferSize > 100 * 1024 * 1024)
- {
- return false;
- }
-
- var newBuffer = new byte[newBufferSize];
- buffer.CopyTo(newBuffer, 0);
- buffer = newBuffer;
-
- return true;
- }
-
private PrometheusMetric GetPrometheusMetric(Metric metric)
{
// Optimize writing metrics with bounded cache that has pre-calculated Prometheus names.
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs
index c5b0e0e64d3..2d9ea4c27d1 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs
@@ -14,8 +14,6 @@ namespace OpenTelemetry.Exporter.Prometheus;
[ExportModes(ExportModes.Pull)]
internal sealed class PrometheusExporter : BaseExporter, IPullMetricExporter
{
- private Func? funcCollect;
- private ExportFunc? funcExport;
private Resource? resource;
private bool disposed;
@@ -38,17 +36,9 @@ public PrometheusExporter(PrometheusExporterOptions options)
///
/// Gets or sets the Collect delegate.
///
- public Func? Collect
- {
- get => this.funcCollect;
- set => this.funcCollect = value;
- }
+ public Func? Collect { get; set; }
- internal ExportFunc? OnExport
- {
- get => this.funcExport;
- set => this.funcExport = value;
- }
+ internal ExportFunc? OnExport { get; set; }
internal Action? OnDispose { get; set; }
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs
index cc9176ea0c5..9f80d6dc5d3 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs
@@ -16,10 +16,10 @@ Histogram becomes histogram
UpDownCounter becomes gauge
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#otlp-metric-points-to-prometheus
*/
- private static readonly PrometheusType[] MetricTypes = new PrometheusType[]
- {
+ private static readonly PrometheusType[] MetricTypes =
+ [
PrometheusType.Untyped, PrometheusType.Counter, PrometheusType.Gauge, PrometheusType.Summary, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Gauge,
- };
+ ];
public PrometheusMetric(string name, string unit, PrometheusType type, bool disableTotalNameSuffixForCounters)
{
@@ -40,7 +40,7 @@ public PrometheusMetric(string name, string unit, PrometheusType type, bool disa
// [OpenMetrics UNIT metadata](https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#metricfamily)
// and as a suffix to the metric name. The unit suffix comes before any type-specific suffixes.
// https://github.com/open-telemetry/opentelemetry-specification/blob/3dfb383fe583e3b74a2365c5a1d90256b273ee76/specification/compatibility/prometheus_and_openmetrics.md#metric-metadata-1
- if (!sanitizedName.EndsWith(sanitizedUnit))
+ if (!sanitizedName.EndsWith(sanitizedUnit, StringComparison.Ordinal))
{
sanitizedName += $"_{sanitizedUnit}";
openMetricsName += $"_{sanitizedUnit}";
@@ -51,14 +51,14 @@ public PrometheusMetric(string name, string unit, PrometheusType type, bool disa
// Exporters SHOULD provide a configuration option to disable the addition of `_total` suffixes.
// https://github.com/open-telemetry/opentelemetry-specification/blob/b2f923fb1650dde1f061507908b834035506a796/specification/compatibility/prometheus_and_openmetrics.md#L286
// Note that we no longer append '_ratio' for units that are '1', see: https://github.com/open-telemetry/opentelemetry-specification/issues/4058
- if (type == PrometheusType.Counter && !sanitizedName.EndsWith("_total") && !disableTotalNameSuffixForCounters)
+ if (type == PrometheusType.Counter && !sanitizedName.EndsWith("_total", StringComparison.Ordinal) && !disableTotalNameSuffixForCounters)
{
sanitizedName += "_total";
}
// For counters requested using OpenMetrics format, the MetricFamily name MUST be suffixed with '_total', regardless of the setting to disable the 'total' suffix.
// https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#counter-1
- if (type == PrometheusType.Counter && !openMetricsName.EndsWith("_total"))
+ if (type == PrometheusType.Counter && !openMetricsName.EndsWith("_total", StringComparison.Ordinal))
{
openMetricsName += "_total";
}
@@ -127,7 +127,7 @@ internal static string SanitizeMetricName(string metricName)
return sb?.ToString() ?? metricName;
- static StringBuilder CreateStringBuilder(string name) => new StringBuilder(name.Length);
+ static StringBuilder CreateStringBuilder(string name) => new(name.Length);
}
internal static string RemoveAnnotations(string unit)
@@ -179,7 +179,7 @@ internal static string RemoveAnnotations(string unit)
private static string SanitizeOpenMetricsName(string metricName)
{
- if (metricName.EndsWith("_total"))
+ if (metricName.EndsWith("_total", StringComparison.Ordinal))
{
return metricName.Substring(0, metricName.Length - 6);
}
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs
index b182b521506..c199a6695c5 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs
@@ -17,8 +17,6 @@ internal static partial class PrometheusSerializer
{
#pragma warning disable SA1310 // Field name should not contain an underscore
private const byte ASCII_QUOTATION_MARK = 0x22; // '"'
- private const byte ASCII_FULL_STOP = 0x2E; // '.'
- private const byte ASCII_HYPHEN_MINUS = 0x2D; // '-'
private const byte ASCII_REVERSE_SOLIDUS = 0x5C; // '\\'
private const byte ASCII_LINEFEED = 0x0A; // `\n`
#pragma warning restore SA1310 // Field name should not contain an underscore
@@ -102,16 +100,13 @@ public static int WriteUnicodeNoEscape(byte[] buffer, int cursor, ushort ordinal
buffer[cursor++] = unchecked((byte)(0b_1100_0000 | (ordinal >> 6)));
buffer[cursor++] = unchecked((byte)(0b_1000_0000 | (ordinal & 0b_0011_1111)));
}
- else if (ordinal <= 0xFFFF)
+ else
{
+ // all other <= 0xFFFF which is ushort.MaxValue
buffer[cursor++] = unchecked((byte)(0b_1110_0000 | (ordinal >> 12)));
buffer[cursor++] = unchecked((byte)(0b_1000_0000 | ((ordinal >> 6) & 0b_0011_1111)));
buffer[cursor++] = unchecked((byte)(0b_1000_0000 | (ordinal & 0b_0011_1111)));
}
- else
- {
- Debug.Assert(ordinal <= 0xFFFF, ".NET string should not go beyond Unicode BMP.");
- }
return cursor;
}
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj
index 4e087919be2..f69ce360219 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj
@@ -23,4 +23,10 @@
+
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs
index cecda73f7c9..b3bdf286f34 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs
@@ -30,12 +30,20 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene
string path = options.ScrapeEndpointPath ?? PrometheusHttpListenerOptions.DefaultScrapeEndpointPath;
- if (!path.StartsWith("/"))
+#if NET
+ if (!path.StartsWith('/'))
+#else
+ if (!path.StartsWith("/", StringComparison.Ordinal))
+#endif
{
path = $"/{path}";
}
- if (!path.EndsWith("/"))
+#if NET
+ if (!path.EndsWith('/'))
+#else
+ if (!path.EndsWith("/", StringComparison.Ordinal))
+#endif
{
path = $"{path}/";
}
@@ -164,7 +172,11 @@ private async Task ProcessRequestAsync(HttpListenerContext context)
? "application/openmetrics-text; version=1.0.0; charset=utf-8"
: "text/plain; charset=utf-8; version=0.0.4";
- await context.Response.OutputStream.WriteAsync(dataView.Array!, 0, dataView.Count).ConfigureAwait(false);
+#if NET
+ await context.Response.OutputStream.WriteAsync(dataView.Array.AsMemory(0, dataView.Count)).ConfigureAwait(false);
+#else
+ await context.Response.OutputStream.WriteAsync(dataView.Array, 0, dataView.Count).ConfigureAwait(false);
+#endif
}
else
{
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs
index 7289432cdcf..94eea0f5742 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs
@@ -62,7 +62,7 @@ public static MeterProviderBuilder AddPrometheusHttpListener(
});
}
- private static MetricReader BuildPrometheusHttpListenerMetricReader(
+ private static BaseExportingMetricReader BuildPrometheusHttpListenerMetricReader(
PrometheusHttpListenerOptions options)
{
var exporter = new PrometheusExporter(new PrometheusExporterOptions
@@ -78,8 +78,10 @@ private static MetricReader BuildPrometheusHttpListenerMetricReader(
try
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
var listener = new PrometheusHttpListener(exporter, options);
- exporter.OnDispose = () => listener.Dispose();
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ exporter.OnDispose = listener.Dispose;
listener.Start();
}
catch (Exception ex)
diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs
index dbe20b726c4..8909a3bd82e 100644
--- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs
+++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs
@@ -12,7 +12,7 @@ public class PrometheusHttpListenerOptions
{
internal const string DefaultScrapeEndpointPath = "/metrics";
- private IReadOnlyCollection uriPrefixes = new[] { "http://localhost:9464/" };
+ private IReadOnlyCollection uriPrefixes = ["http://localhost:9464/"];
///
/// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics".
diff --git a/src/OpenTelemetry.Exporter.Zipkin/AssemblyInfo.cs b/src/OpenTelemetry.Exporter.Zipkin/AssemblyInfo.cs
deleted file mode 100644
index 7ce3243549d..00000000000
--- a/src/OpenTelemetry.Exporter.Zipkin/AssemblyInfo.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Zipkin.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-[assembly: InternalsVisibleTo("Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Zipkin.Tests")]
-[assembly: InternalsVisibleTo("Benchmarks")]
-#endif
diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md
index bd3cb442aeb..f22f4de85d9 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md
@@ -6,6 +6,27 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+* Removed the peer service resolver, which was based on earlier experimental
+ semantic conventions that are not part of the stable specification. This
+ change ensures that the exporter no longer modifies or assumes the value of
+ peer service attributes.
+ ([#6191](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6191))
+
+* Extended remote endpoint calculation to align with the [opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.40.0/specification/trace/sdk_exporters/zipkin.md#otlp---zipkin).
+ ([#6191](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6191))
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
@@ -209,7 +230,7 @@ Released 2022-June-1
([#3281](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3281))
* Fix exporting of array-valued attributes on an `Activity`. Previously, each
item in the array would result in a new tag on an exported `Activity`. Now,
- array-valued attributes are serialzed to a JSON-array representation.
+ array-valued attributes are serialized to a JSON-array representation.
([#3281](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3281))
## 1.3.0-beta.2
diff --git a/src/Shared/PooledList.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/PooledList.cs
similarity index 100%
rename from src/Shared/PooledList.cs
rename to src/OpenTelemetry.Exporter.Zipkin/Implementation/PooledList.cs
diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs
index 2753f51f50a..247cd87463b 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs
@@ -20,103 +20,15 @@ internal static class ZipkinActivityConversionExtensions
internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint localEndpoint, bool useShortTraceIds = false)
{
var context = activity.Context;
+ string? parentId = activity.ParentSpanId == default ? null : EncodeSpanId(activity.ParentSpanId);
- string? parentId = activity.ParentSpanId == default ?
- null
- : EncodeSpanId(activity.ParentSpanId);
+ var tags = PooledList>.Create();
+ ExtractActivityTags(activity, ref tags);
+ ExtractActivityStatus(activity, ref tags);
+ ExtractActivitySource(activity, ref tags);
- var tagState = new TagEnumerationState
- {
- Tags = PooledList>.Create(),
- };
-
- tagState.EnumerateTags(activity);
-
- // When status is set on Activity using the native Status field in activity,
- // which was first introduced in System.Diagnostic.DiagnosticSource 6.0.0.
- if (activity.Status != ActivityStatusCode.Unset)
- {
- if (activity.Status == ActivityStatusCode.Ok)
- {
- PooledList>.Add(
- ref tagState.Tags,
- new KeyValuePair(
- SpanAttributeConstants.StatusCodeKey,
- "OK"));
- }
-
- // activity.Status is Error
- else
- {
- PooledList>.Add(
- ref tagState.Tags,
- new KeyValuePair(
- SpanAttributeConstants.StatusCodeKey,
- "ERROR"));
-
- // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status
- PooledList>.Add(
- ref tagState.Tags,
- new KeyValuePair(
- ZipkinErrorFlagTagName,
- activity.StatusDescription ?? string.Empty));
- }
- }
-
- // In the case when both activity status and status tag were set,
- // activity status takes precedence over status tag.
- else if (tagState.StatusCode.HasValue && tagState.StatusCode != StatusCode.Unset)
- {
- PooledList>.Add(
- ref tagState.Tags,
- new KeyValuePair(
- SpanAttributeConstants.StatusCodeKey,
- StatusHelper.GetTagValueForStatusCode(tagState.StatusCode.Value)));
-
- // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status
- if (tagState.StatusCode == StatusCode.Error)
- {
- PooledList>.Add(
- ref tagState.Tags,
- new KeyValuePair(
- ZipkinErrorFlagTagName,
- tagState.StatusDescription ?? string.Empty));
- }
- }
-
- var activitySource = activity.Source;
- if (!string.IsNullOrEmpty(activitySource.Name))
- {
- PooledList>.Add(ref tagState.Tags, new KeyValuePair("otel.scope.name", activitySource.Name));
-
- // otel.library.name is deprecated, but has to be propagated according to https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/common/mapping-to-non-otlp.md#instrumentationscope
- PooledList>.Add(ref tagState.Tags, new KeyValuePair("otel.library.name", activitySource.Name));
- if (!string.IsNullOrEmpty(activitySource.Version))
- {
- PooledList>.Add(ref tagState.Tags, new KeyValuePair("otel.scope.version", activitySource.Version));
-
- // otel.library.version is deprecated, but has to be propagated according to https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/common/mapping-to-non-otlp.md#instrumentationscope
- PooledList>.Add(ref tagState.Tags, new KeyValuePair("otel.library.version", activitySource.Version));
- }
- }
-
- ZipkinEndpoint? remoteEndpoint = null;
- if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
- {
- PeerServiceResolver.Resolve(ref tagState, out string? peerServiceName, out bool addAsTag);
-
- if (peerServiceName != null)
- {
- remoteEndpoint = RemoteEndpointCache.GetOrAdd((peerServiceName, default), ZipkinEndpoint.Create);
- if (addAsTag)
- {
- PooledList>.Add(ref tagState.Tags, new KeyValuePair(SemanticConventions.AttributePeerService, peerServiceName));
- }
- }
- }
-
- EventEnumerationState eventState = default;
- eventState.EnumerateEvents(activity);
+ ZipkinEndpoint? remoteEndpoint = ExtractRemoteEndpoint(activity);
+ var annotations = ExtractActivityEvents(activity);
return new ZipkinSpan(
EncodeTraceId(context.TraceId, useShortTraceIds),
@@ -128,8 +40,8 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l
duration: activity.Duration.ToEpochMicroseconds(),
localEndpoint,
remoteEndpoint,
- eventState.Annotations,
- tagState.Tags,
+ annotations,
+ tags,
null,
null);
}
@@ -184,89 +96,235 @@ private static string EncodeTraceId(ActivityTraceId traceId, bool useShortTraceI
};
}
- internal struct TagEnumerationState : PeerServiceResolver.IPeerServiceState
+ private static string ExtractStatusDescription(Activity activity)
{
- public PooledList> Tags;
-
- public string? PeerService { get; set; }
-
- public int? PeerServicePriority { get; set; }
-
- public string? HostName { get; set; }
-
- public string? IpAddress { get; set; }
+ return activity.StatusDescription
+ ?? activity.GetTagItem(SpanAttributeConstants.StatusDescriptionKey) as string
+ ?? activity.GetTagItem(ZipkinErrorFlagTagName) as string
+ ?? string.Empty;
+ }
- public long Port { get; set; }
+ private static void ExtractActivityTags(Activity activity, ref PooledList> tags)
+ {
+ foreach (ref readonly var tag in activity.EnumerateTagObjects())
+ {
+ if (tag.Key != ZipkinErrorFlagTagName && tag.Key != SpanAttributeConstants.StatusCodeKey)
+ {
+ PooledList>.Add(ref tags, tag);
+ }
+ }
+ }
- public StatusCode? StatusCode { get; set; }
+ private static void ExtractActivityStatus(Activity activity, ref PooledList> tags)
+ {
+ // When status is set on Activity using the native Status field in activity,
+ // which was first introduced in System.Diagnostic.DiagnosticSource 6.0.0.
+ if (activity.Status != ActivityStatusCode.Unset)
+ {
+ if (activity.Status == ActivityStatusCode.Ok)
+ {
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ SpanAttributeConstants.StatusCodeKey,
+ "OK"));
+ }
- public string StatusDescription { get; set; }
+ // activity.Status is Error
+ else
+ {
+ string statusDescription = ExtractStatusDescription(activity);
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ SpanAttributeConstants.StatusCodeKey,
+ "ERROR"));
- public void EnumerateTags(Activity activity)
+ // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ ZipkinErrorFlagTagName,
+ statusDescription));
+ }
+ }
+ else
{
- foreach (ref readonly var tag in activity.EnumerateTagObjects())
+ if (activity.GetTagItem(SpanAttributeConstants.StatusCodeKey) is string status)
{
- if (tag.Value == null)
+ if (status == "OK")
{
- continue;
- }
-
- string key = tag.Key;
+ activity.SetStatus(ActivityStatusCode.Ok);
- if (tag.Value is string strVal)
- {
- PeerServiceResolver.InspectTag(ref this, key, strVal);
-
- if (key == SpanAttributeConstants.StatusCodeKey)
- {
- this.StatusCode = StatusHelper.GetStatusCodeForTagValue(strVal);
- continue;
- }
- else if (key == SpanAttributeConstants.StatusDescriptionKey)
- {
- // Description is sent as `error` but only if StatusCode is Error. See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status
- this.StatusDescription = strVal;
- continue;
- }
- else if (key == ZipkinErrorFlagTagName)
- {
- // Ignore `error` tag if it exists, it will be added based on StatusCode + StatusDescription.
- continue;
- }
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ SpanAttributeConstants.StatusCodeKey,
+ "OK"));
}
- else if (tag.Value is int intVal && tag.Key == SemanticConventions.AttributeNetPeerPort)
+ else if (status == "ERROR")
{
- PeerServiceResolver.InspectTag(ref this, key, intVal);
- }
+ string statusDescription = ExtractStatusDescription(activity);
+
+ activity.SetStatus(ActivityStatusCode.Error);
+
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ SpanAttributeConstants.StatusCodeKey,
+ "ERROR"));
- PooledList>.Add(ref this.Tags, tag);
+ PooledList>.Add(
+ ref tags,
+ new KeyValuePair(
+ ZipkinErrorFlagTagName,
+ statusDescription));
+ }
}
}
}
- private struct EventEnumerationState
+ private static void ExtractActivitySource(Activity activity, ref PooledList> tags)
{
- public bool Created;
+ var source = activity.Source;
+ if (!string.IsNullOrEmpty(source.Name))
+ {
+ PooledList>.Add(ref tags, new KeyValuePair("otel.scope.name", source.Name));
+
+ // otel.library.name is deprecated, but has to be propagated according to https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/common/mapping-to-non-otlp.md#instrumentationscope
+ PooledList>.Add(ref tags, new KeyValuePair("otel.library.name", source.Name));
+
+ if (!string.IsNullOrEmpty(source.Version))
+ {
+ PooledList>.Add(ref tags, new KeyValuePair("otel.scope.version", source.Version));
- public PooledList Annotations;
+ // otel.library.version is deprecated, but has to be propagated according to https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/common/mapping-to-non-otlp.md#instrumentationscope
+ PooledList>.Add(ref tags, new KeyValuePair("otel.library.version", source.Version));
+ }
+ }
+ }
- public void EnumerateEvents(Activity activity)
+ private static ZipkinEndpoint? ExtractRemoteEndpoint(Activity activity)
+ {
+ if (activity.Kind != ActivityKind.Client && activity.Kind != ActivityKind.Producer)
{
- var enumerator = activity.EnumerateEvents();
+ return null;
+ }
- if (enumerator.MoveNext())
+ static ZipkinEndpoint? TryCreateEndpoint(string? remoteEndpoint)
+ {
+ if (remoteEndpoint != null)
{
- this.Annotations = PooledList.Create();
- this.Created = true;
+ var endpoint = RemoteEndpointCache.GetOrAdd((remoteEndpoint, default), ZipkinEndpoint.Create);
+ return endpoint;
+ }
- do
- {
- ref readonly var @event = ref enumerator.Current;
+ return null;
+ }
- PooledList.Add(ref this.Annotations, new ZipkinAnnotation(@event.Timestamp.ToEpochMicroseconds(), @event.Name));
- }
- while (enumerator.MoveNext());
- }
+ string? remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributePeerService) as string;
+ var endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
}
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributeServerAddress) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributeNetPeerName) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ var peerAddress = activity.GetTagItem(SemanticConventions.AttributeNetworkPeerAddress) as string;
+ var peerPort = activity.GetTagItem(SemanticConventions.AttributeNetworkPeerPort) as string;
+ remoteEndpoint = peerPort != null ? $"{peerAddress}:{peerPort}" : peerAddress;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributeServerSocketDomain) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ var serverAddress = activity.GetTagItem(SemanticConventions.AttributeServerSocketAddress) as string;
+ var serverPort = activity.GetTagItem(SemanticConventions.AttributeServerSocketPort) as string;
+ remoteEndpoint = serverPort != null ? $"{serverAddress}:{serverPort}" : serverAddress;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributeNetSockPeerName) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ var socketAddress = activity.GetTagItem(SemanticConventions.AttributeNetSockPeerAddr) as string;
+ var socketPort = activity.GetTagItem(SemanticConventions.AttributeNetSockPeerPort) as string;
+ remoteEndpoint = socketPort != null ? $"{socketAddress}:{socketPort}" : socketAddress;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributePeerHostname) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributePeerAddress) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ remoteEndpoint = activity.GetTagItem(SemanticConventions.AttributeDbName) as string;
+ endpoint = TryCreateEndpoint(remoteEndpoint);
+ if (endpoint != null)
+ {
+ return endpoint;
+ }
+
+ return null;
+ }
+
+ private static PooledList ExtractActivityEvents(Activity activity)
+ {
+ var enumerator = activity.EnumerateEvents().GetEnumerator();
+ if (!enumerator.MoveNext())
+ {
+ return default;
+ }
+
+ var annotations = PooledList.Create();
+
+ do
+ {
+ var @event = enumerator.Current;
+ PooledList.Add(ref annotations, new ZipkinAnnotation(@event.Timestamp.ToEpochMicroseconds(), @event.Name));
+ }
+ while (enumerator.MoveNext());
+
+ return annotations;
}
}
diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs
index 30e1eb112ee..3e1c42c69ec 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs
+++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinTagWriter.cs
@@ -64,4 +64,6 @@ protected override void OnUnsupportedTagDropped(
tagValueTypeFullName,
tagKey);
}
+
+ protected override bool TryWriteEmptyTag(ref Utf8JsonWriter state, string key, object? value) => false;
}
diff --git a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj
index 78d44fd3ee5..a9f9e7df834 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj
+++ b/src/OpenTelemetry.Exporter.Zipkin/OpenTelemetry.Exporter.Zipkin.csproj
@@ -11,13 +11,11 @@
-
-
@@ -33,4 +31,9 @@
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporter.cs b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporter.cs
index c65f1454284..103cacbcea5 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporter.cs
+++ b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporter.cs
@@ -225,6 +225,17 @@ protected override bool TryComputeLength(out long length)
return false;
}
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ this.writer?.Dispose();
+ this.writer = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SerializeToStreamInternal(Stream stream)
{
diff --git a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs
index b7142b985a5..869202b0f8d 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs
+++ b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterHelperExtensions.cs
@@ -68,12 +68,11 @@ public static TracerProviderBuilder AddZipkinExporter(
{
var options = sp.GetRequiredService>().Get(name);
- return BuildZipkinExporterProcessor(builder, options, sp);
+ return BuildZipkinExporterProcessor(options, sp);
});
}
private static BaseProcessor BuildZipkinExporterProcessor(
- TracerProviderBuilder builder,
ZipkinExporterOptions options,
IServiceProvider serviceProvider)
{
@@ -91,7 +90,7 @@ private static BaseProcessor BuildZipkinExporterProcessor(
"CreateClient",
BindingFlags.Public | BindingFlags.Instance,
binder: null,
- new Type[] { typeof(string) },
+ [typeof(string)],
modifiers: null);
if (createClientMethod != null)
{
@@ -106,20 +105,17 @@ private static BaseProcessor BuildZipkinExporterProcessor(
};
}
+#pragma warning disable CA2000 // Dispose objects before losing scope
var zipkinExporter = new ZipkinExporter(options);
+#pragma warning restore CA2000 // Dispose objects before losing scope
- if (options.ExportProcessorType == ExportProcessorType.Simple)
- {
- return new SimpleActivityExportProcessor(zipkinExporter);
- }
- else
- {
- return new BatchActivityExportProcessor(
+ return options.ExportProcessorType == ExportProcessorType.Simple
+ ? new SimpleActivityExportProcessor(zipkinExporter)
+ : new BatchActivityExportProcessor(
zipkinExporter,
options.BatchExportProcessorOptions.MaxQueueSize,
options.BatchExportProcessorOptions.ScheduledDelayMilliseconds,
options.BatchExportProcessorOptions.ExporterTimeoutMilliseconds,
options.BatchExportProcessorOptions.MaxExportBatchSize);
- }
}
}
diff --git a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs
index 1f4bcd14684..161ab174b2f 100644
--- a/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs
+++ b/src/OpenTelemetry.Exporter.Zipkin/ZipkinExporterOptions.cs
@@ -42,7 +42,7 @@ internal ZipkinExporterOptions(
if (configuration!.TryGetUriValue(ZipkinExporterEventSource.Log, ZipkinEndpointEnvVar, out var endpoint))
{
- this.Endpoint = endpoint!;
+ this.Endpoint = endpoint;
}
this.BatchExportProcessorOptions = defaultBatchOptions!;
diff --git a/src/OpenTelemetry.Extensions.Hosting/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.Hosting/AssemblyInfo.cs
deleted file mode 100644
index d36465c0505..00000000000
--- a/src/OpenTelemetry.Extensions.Hosting/AssemblyInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)]
-
-#if SIGNED
-file static class AssemblyInfo
-{
- public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
-}
-#else
-file static class AssemblyInfo
-{
- public const string PublicKey = "";
-}
-#endif
diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md
index 635c5845252..3201a20f9f3 100644
--- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md
+++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md
@@ -6,6 +6,18 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs
index 1d31393b0fa..af4bb57a7f2 100644
--- a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs
+++ b/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs
@@ -11,7 +11,7 @@ namespace OpenTelemetry.Extensions.Hosting.Implementation;
[EventSource(Name = "OpenTelemetry-Extensions-Hosting")]
internal sealed class HostingExtensionsEventSource : EventSource
{
- public static HostingExtensionsEventSource Log = new();
+ public static readonly HostingExtensionsEventSource Log = new();
[Event(1, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)]
public void TracerProviderNotRegistered()
diff --git a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj
index ee51a4f8543..22d961e6a88 100644
--- a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj
+++ b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj
@@ -5,7 +5,6 @@
Contains extensions to start OpenTelemetry in applications using Microsoft.Extensions.Hosting
OpenTelemetry
core-
- latest-all
@@ -16,4 +15,8 @@
+
+
+
+
diff --git a/src/OpenTelemetry.Extensions.Propagators/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.Propagators/AssemblyInfo.cs
deleted file mode 100644
index a06279f44f2..00000000000
--- a/src/OpenTelemetry.Extensions.Propagators/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Propagators.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Propagators.Tests")]
-#endif
diff --git a/src/OpenTelemetry.Extensions.Propagators/B3Propagator.cs b/src/OpenTelemetry.Extensions.Propagators/B3Propagator.cs
index 45239981f0a..a7ede4f0a6a 100644
--- a/src/OpenTelemetry.Extensions.Propagators/B3Propagator.cs
+++ b/src/OpenTelemetry.Extensions.Propagators/B3Propagator.cs
@@ -27,6 +27,7 @@ public sealed class B3Propagator : TextMapPropagator
internal const string UpperTraceId = "0000000000000000";
// Sampled values via the X_B3_SAMPLED header.
+ internal const char SampledValueChar = '1';
internal const string SampledValue = "1";
// Some old zipkin implementations may send true/false for the sampled header. Only use this for checking incoming values.
@@ -35,7 +36,7 @@ public sealed class B3Propagator : TextMapPropagator
// "Debug" sampled value.
internal const string FlagsValue = "1";
- private static readonly HashSet AllFields = new() { XB3TraceId, XB3SpanId, XB3ParentSpanId, XB3Sampled, XB3Flags };
+ private static readonly HashSet AllFields = [XB3TraceId, XB3SpanId, XB3ParentSpanId, XB3Sampled, XB3Flags];
private static readonly HashSet SampledValues = new(StringComparer.Ordinal) { SampledValue, LegacySampledValue };
@@ -82,14 +83,7 @@ public override PropagationContext Extract(PropagationContext context, T carr
return context;
}
- if (this.singleHeader)
- {
- return ExtractFromSingleHeader(context, carrier, getter);
- }
- else
- {
- return ExtractFromMultipleHeaders(context, carrier, getter);
- }
+ return this.singleHeader ? ExtractFromSingleHeader(context, carrier, getter) : ExtractFromMultipleHeaders(context, carrier, getter);
}
///
@@ -122,7 +116,7 @@ public override void Inject(PropagationContext context, T carrier, Action(PropagationContext
{
try
{
- var header = getter(carrier, XB3Combined)?.FirstOrDefault();
+ var headers = getter(carrier, XB3Combined);
+ if (headers == null)
+ {
+ return context;
+ }
+
+ var header = headers.FirstOrDefault();
+
if (string.IsNullOrWhiteSpace(header))
{
return context;
}
- var parts = header!.Split(XB3CombinedDelimiter);
+ var parts = header.Split(XB3CombinedDelimiter);
if (parts.Length < 2 || parts.Length > 4)
{
return context;
diff --git a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md
index f340ca81f54..2170045f1d7 100644
--- a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md
+++ b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md
@@ -6,6 +6,18 @@ covering all components see: [Release Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry.Extensions.Propagators/JaegerPropagator.cs b/src/OpenTelemetry.Extensions.Propagators/JaegerPropagator.cs
index 045fd93e581..d11d8d67912 100644
--- a/src/OpenTelemetry.Extensions.Propagators/JaegerPropagator.cs
+++ b/src/OpenTelemetry.Extensions.Propagators/JaegerPropagator.cs
@@ -17,7 +17,7 @@ public class JaegerPropagator : TextMapPropagator
internal const string JaegerDelimiterEncoded = "%3A"; // while the spec defines the delimiter as a ':', some clients will url encode headers.
internal const string SampledValue = "1";
- internal static readonly string[] JaegerDelimiters = { JaegerDelimiter, JaegerDelimiterEncoded };
+ internal static readonly string[] JaegerDelimiters = [JaegerDelimiter, JaegerDelimiterEncoded];
private static readonly int TraceId128BitLength = "0af7651916cd43dd8448eb211c80319c".Length;
private static readonly int SpanIdLength = "00f067aa0ba902b7".Length;
@@ -49,14 +49,19 @@ public override PropagationContext Extract(PropagationContext context, T carr
try
{
var jaegerHeaderCollection = getter(carrier, JaegerHeader);
- var jaegerHeader = jaegerHeaderCollection?.First();
+ if (jaegerHeaderCollection == null)
+ {
+ return context;
+ }
+
+ var jaegerHeader = jaegerHeaderCollection.First();
if (string.IsNullOrWhiteSpace(jaegerHeader))
{
return context;
}
- var jaegerHeaderParsed = TryExtractTraceContext(jaegerHeader!, out var traceId, out var spanId, out var traceOptions);
+ var jaegerHeaderParsed = TryExtractTraceContext(jaegerHeader, out var traceId, out var spanId, out var traceOptions);
if (!jaegerHeaderParsed)
{
@@ -154,7 +159,7 @@ internal static bool TryExtractTraceContext(string jaegerHeader, out ActivityTra
spanId = ActivitySpanId.CreateFromString(spanIdStr.AsSpan());
var traceFlagsStr = traceComponents[3];
- if (SampledValue.Equals(traceFlagsStr))
+ if (SampledValue.Equals(traceFlagsStr, StringComparison.Ordinal))
{
traceOptions |= ActivityTraceFlags.Recorded;
}
diff --git a/src/OpenTelemetry.Extensions.Propagators/OpenTelemetry.Extensions.Propagators.csproj b/src/OpenTelemetry.Extensions.Propagators/OpenTelemetry.Extensions.Propagators.csproj
index 01ae76f4a17..34f57c8043e 100644
--- a/src/OpenTelemetry.Extensions.Propagators/OpenTelemetry.Extensions.Propagators.csproj
+++ b/src/OpenTelemetry.Extensions.Propagators/OpenTelemetry.Extensions.Propagators.csproj
@@ -12,7 +12,11 @@
- $(NoWarn),1591
+ $(NoWarn),CS1591
+
+
+
+
diff --git a/src/OpenTelemetry.Shims.OpenTracing/AssemblyInfo.cs b/src/OpenTelemetry.Shims.OpenTracing/AssemblyInfo.cs
deleted file mode 100644
index ae3704ebf27..00000000000
--- a/src/OpenTelemetry.Shims.OpenTracing/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-#if SIGNED
-[assembly: InternalsVisibleTo("OpenTelemetry.Shims.OpenTracing.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
-#else
-[assembly: InternalsVisibleTo("OpenTelemetry.Shims.OpenTracing.Tests")]
-#endif
diff --git a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md
index 65dcca4f66f..67954363842 100644
--- a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md
+++ b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md
@@ -6,6 +6,20 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.12.0-beta.1
+
+Released 2025-May-06
+
+* Updated OpenTelemetry core component version(s) to `1.12.0`.
+ ([#6269](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6269))
+
+## 1.11.2-beta.1
+
+Released 2025-Mar-05
+
+* Updated OpenTelemetry core component version(s) to `1.11.2`.
+ ([#6169](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6169))
+
## 1.11.0-beta.1
Released 2025-Jan-16
diff --git a/src/OpenTelemetry.Shims.OpenTracing/OpenTelemetry.Shims.OpenTracing.csproj b/src/OpenTelemetry.Shims.OpenTracing/OpenTelemetry.Shims.OpenTracing.csproj
index 127cbfb3aa9..4262f637d14 100644
--- a/src/OpenTelemetry.Shims.OpenTracing/OpenTelemetry.Shims.OpenTracing.csproj
+++ b/src/OpenTelemetry.Shims.OpenTracing/OpenTelemetry.Shims.OpenTracing.csproj
@@ -24,4 +24,8 @@
+
+
+
+
diff --git a/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs b/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs
index f95b1b77be3..8f256920127 100644
--- a/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs
+++ b/src/OpenTelemetry.Shims.OpenTracing/SpanBuilderShim.cs
@@ -27,7 +27,7 @@ internal sealed class SpanBuilderShim : ISpanBuilder
///
/// The OpenTelemetry links. These correspond loosely to OpenTracing references.
///
- private readonly List links = new();
+ private readonly List links = [];
///
/// The OpenTelemetry attributes. These correspond to OpenTracing Tags.
@@ -65,7 +65,7 @@ public SpanBuilderShim(Tracer tracer, string spanName)
this.ScopeManager = new ScopeManagerShim();
}
- private IScopeManager ScopeManager { get; }
+ private ScopeManagerShim ScopeManager { get; }
private bool ParentSet => this.parentSpan != null || this.parentSpanContext.IsValid;
@@ -148,10 +148,7 @@ public ISpan Start()
span = this.tracer.StartSpan(this.spanName, this.spanKind, this.parentSpanContext, this.attributes, this.links, this.explicitStartTime ?? default);
}
- if (span == null)
- {
- span = this.tracer.StartSpan(this.spanName, this.spanKind, default(SpanContext), this.attributes, null, this.explicitStartTime ?? default);
- }
+ span ??= this.tracer.StartSpan(this.spanName, this.spanKind, default(SpanContext), this.attributes, null, this.explicitStartTime ?? default);
if (this.error)
{
diff --git a/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs b/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs
index c1bde927a1b..f8a5d981207 100644
--- a/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs
+++ b/src/OpenTelemetry.Shims.OpenTracing/SpanShim.cs
@@ -14,8 +14,8 @@ internal sealed class SpanShim : ISpan
///
public const string DefaultEventName = "log";
- private static readonly IReadOnlyCollection OpenTelemetrySupportedAttributeValueTypes = new List
- {
+ private static readonly IReadOnlyCollection OpenTelemetrySupportedAttributeValueTypes =
+ [
typeof(string),
typeof(bool),
typeof(byte),
@@ -24,7 +24,7 @@ internal sealed class SpanShim : ISpan
typeof(long),
typeof(float),
typeof(double),
- };
+ ];
private readonly SpanContextShim spanContextShim;
diff --git a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs
index 504b28c5a52..b67dfd9a82a 100644
--- a/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs
+++ b/src/OpenTelemetry.Shims.OpenTracing/TracerShim.cs
@@ -48,13 +48,7 @@ public TracerShim(Trace.TracerProvider tracerProvider, TextMapPropagator? textFo
///
public global::OpenTracing.ISpan? ActiveSpan => this.ScopeManager.Active?.Span;
- private TextMapPropagator Propagator
- {
- get
- {
- return this.definedPropagator ?? Propagators.DefaultTextMapPropagator;
- }
- }
+ private TextMapPropagator Propagator => this.definedPropagator ?? Propagators.DefaultTextMapPropagator;
///
public global::OpenTracing.ISpanBuilder BuildSpan(string operationName)
@@ -76,7 +70,7 @@ private TextMapPropagator Propagator
foreach (var entry in textMapCarrier)
{
- carrierMap.Add(entry.Key, new[] { entry.Value });
+ carrierMap.Add(entry.Key, [entry.Value]);
}
static IEnumerable? GetCarrierKeyValue(Dictionary> source, string key)
diff --git a/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt
index 7c9a6d046ae..b9507b58b38 100644
--- a/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt
@@ -1,27 +1,27 @@
-abstract OpenTelemetry.Metrics.ExemplarReservoir.Collect() -> OpenTelemetry.Metrics.ReadOnlyExemplarCollection
-abstract OpenTelemetry.Metrics.ExemplarReservoir.Offer(in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
-abstract OpenTelemetry.Metrics.ExemplarReservoir.Offer(in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
+[OTEL1004]abstract OpenTelemetry.Metrics.ExemplarReservoir.Collect() -> OpenTelemetry.Metrics.ReadOnlyExemplarCollection
+[OTEL1004]abstract OpenTelemetry.Metrics.ExemplarReservoir.Offer(in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
+[OTEL1004]abstract OpenTelemetry.Metrics.ExemplarReservoir.Offer(in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
OpenTelemetry.Logs.LogRecord.Logger.get -> OpenTelemetry.Logs.Logger!
OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverity?
OpenTelemetry.Logs.LogRecord.Severity.set -> void
OpenTelemetry.Logs.LogRecord.SeverityText.get -> string?
OpenTelemetry.Logs.LogRecord.SeverityText.set -> void
-OpenTelemetry.Metrics.ExemplarMeasurement
-OpenTelemetry.Metrics.ExemplarMeasurement.ExemplarMeasurement() -> void
-OpenTelemetry.Metrics.ExemplarMeasurement.Tags.get -> System.ReadOnlySpan>
-OpenTelemetry.Metrics.ExemplarMeasurement.Value.get -> T
-OpenTelemetry.Metrics.ExemplarReservoir
-OpenTelemetry.Metrics.ExemplarReservoir.ResetOnCollect.get -> bool
-OpenTelemetry.Metrics.FixedSizeExemplarReservoir
-OpenTelemetry.Metrics.FixedSizeExemplarReservoir.Capacity.get -> int
-OpenTelemetry.Metrics.FixedSizeExemplarReservoir.FixedSizeExemplarReservoir(int capacity) -> void
-OpenTelemetry.Metrics.FixedSizeExemplarReservoir.UpdateExemplar(int exemplarIndex, in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
-OpenTelemetry.Metrics.FixedSizeExemplarReservoir.UpdateExemplar(int exemplarIndex, in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
+[OTEL1004]OpenTelemetry.Metrics.ExemplarMeasurement
+[OTEL1004]OpenTelemetry.Metrics.ExemplarMeasurement.ExemplarMeasurement() -> void
+[OTEL1004]OpenTelemetry.Metrics.ExemplarMeasurement.Tags.get -> System.ReadOnlySpan>
+[OTEL1004]OpenTelemetry.Metrics.ExemplarMeasurement.Value.get -> T
+[OTEL1004]OpenTelemetry.Metrics.ExemplarReservoir
+[OTEL1004]OpenTelemetry.Metrics.ExemplarReservoir.ResetOnCollect.get -> bool
+[OTEL1004]OpenTelemetry.Metrics.FixedSizeExemplarReservoir
+[OTEL1004]OpenTelemetry.Metrics.FixedSizeExemplarReservoir.Capacity.get -> int
+[OTEL1004]OpenTelemetry.Metrics.FixedSizeExemplarReservoir.FixedSizeExemplarReservoir(int capacity) -> void
+[OTEL1004]OpenTelemetry.Metrics.FixedSizeExemplarReservoir.UpdateExemplar(int exemplarIndex, in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
+[OTEL1004]OpenTelemetry.Metrics.FixedSizeExemplarReservoir.UpdateExemplar(int exemplarIndex, in OpenTelemetry.Metrics.ExemplarMeasurement measurement) -> void
OpenTelemetry.Metrics.MetricStreamConfiguration.ExemplarReservoirFactory.get -> System.Func?
OpenTelemetry.Metrics.MetricStreamConfiguration.ExemplarReservoirFactory.set -> void
-override sealed OpenTelemetry.Metrics.FixedSizeExemplarReservoir.Collect() -> OpenTelemetry.Metrics.ReadOnlyExemplarCollection
-static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
-static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action! configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
-static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configureBuilder, System.Action? configureOptions) -> Microsoft.Extensions.Logging.ILoggingBuilder!
-static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.LoggerProviderBuilder!
-virtual OpenTelemetry.Metrics.FixedSizeExemplarReservoir.OnCollected() -> void
+[OTEL1004]override sealed OpenTelemetry.Metrics.FixedSizeExemplarReservoir.Collect() -> OpenTelemetry.Metrics.ReadOnlyExemplarCollection
+[OTEL1000]static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
+[OTEL1000]static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action! configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
+[OTEL1000]static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configureBuilder, System.Action? configureOptions) -> Microsoft.Extensions.Logging.ILoggingBuilder!
+[OTEL1001]static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.LoggerProviderBuilder!
+[OTEL1004]virtual OpenTelemetry.Metrics.FixedSizeExemplarReservoir.OnCollected() -> void
diff --git a/src/OpenTelemetry/AssemblyInfo.cs b/src/OpenTelemetry/AssemblyInfo.cs
deleted file mode 100644
index 46c323088a6..00000000000
--- a/src/OpenTelemetry/AssemblyInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Console" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.InMemory" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("OpenTelemetry.Tests.Stress.Metrics" + AssemblyInfo.PublicKey)]
-[assembly: InternalsVisibleTo("Benchmarks" + AssemblyInfo.PublicKey)]
-
-#if SIGNED
-file static class AssemblyInfo
-{
- public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
-}
-#else
-file static class AssemblyInfo
-{
- public const string PublicKey = "";
-}
-#endif
diff --git a/src/OpenTelemetry/BaseExportProcessor.cs b/src/OpenTelemetry/BaseExportProcessor.cs
index 2e9fe7619c2..bafa4c117ca 100644
--- a/src/OpenTelemetry/BaseExportProcessor.cs
+++ b/src/OpenTelemetry/BaseExportProcessor.cs
@@ -29,13 +29,17 @@ public enum ExportProcessorType
/// Implements processor that exports telemetry objects.
///
/// The type of telemetry object to be exported.
+#pragma warning disable CA1708 // Identifiers should differ by more than case
public abstract class BaseExportProcessor : BaseProcessor
+#pragma warning restore CA1708 // Identifiers should differ by more than case
where T : class
{
///
/// Gets the exporter used by the processor.
///
+#pragma warning disable CA1051 // Do not declare visible instance fields
protected readonly BaseExporter exporter;
+#pragma warning restore CA1051 // Do not declare visible instance fields
private readonly string friendlyTypeName;
private bool disposed;
@@ -48,7 +52,9 @@ protected BaseExportProcessor(BaseExporter exporter)
{
Guard.ThrowIfNull(exporter);
+#pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1
this.friendlyTypeName = $"{this.GetType().Name}{{{exporter.GetType().Name}}}";
+#pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1
this.exporter = exporter;
}
diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs
index daa7468c2d7..b75cc24d86e 100644
--- a/src/OpenTelemetry/BaseProcessor.cs
+++ b/src/OpenTelemetry/BaseProcessor.cs
@@ -9,7 +9,9 @@ namespace OpenTelemetry;
/// Base processor base class.
///
/// The type of object to be processed.
+#pragma warning disable CA1012 // Abstract types should not have public constructors
public abstract class BaseProcessor : IDisposable
+#pragma warning restore CA1012 // Abstract types should not have public constructors
{
private readonly string typeName;
private int shutdownCount;
diff --git a/src/OpenTelemetry/Batch.cs b/src/OpenTelemetry/Batch.cs
index 033b6ebb35b..4f0e0608dcf 100644
--- a/src/OpenTelemetry/Batch.cs
+++ b/src/OpenTelemetry/Batch.cs
@@ -29,7 +29,9 @@ namespace OpenTelemetry;
public Batch(T[] items, int count)
{
Guard.ThrowIfNull(items);
+#pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1
Guard.ThrowIfOutOfRange(count, min: 0, max: items.Length);
+#pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1
this.items = items;
this.Count = this.targetCount = count;
diff --git a/src/OpenTelemetry/BatchExportProcessor.cs b/src/OpenTelemetry/BatchExportProcessor.cs
index b377d5e89e5..46a64b8e042 100644
--- a/src/OpenTelemetry/BatchExportProcessor.cs
+++ b/src/OpenTelemetry/BatchExportProcessor.cs
@@ -60,7 +60,9 @@ protected BatchExportProcessor(
this.exporterThread = new Thread(this.ExporterProc)
{
IsBackground = true,
+#pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1
Name = $"OpenTelemetry-{nameof(BatchExportProcessor)}-{exporter.GetType().Name}",
+#pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1
};
this.exporterThread.Start();
}
diff --git a/src/OpenTelemetry/BatchExportProcessorOptions.cs b/src/OpenTelemetry/BatchExportProcessorOptions.cs
index 67345e9715c..6695c0c6a56 100644
--- a/src/OpenTelemetry/BatchExportProcessorOptions.cs
+++ b/src/OpenTelemetry/BatchExportProcessorOptions.cs
@@ -26,7 +26,7 @@ public class BatchExportProcessorOptions
public int ExporterTimeoutMilliseconds { get; set; } = BatchExportProcessor.DefaultExporterTimeoutMilliseconds;
///
- /// Gets or sets the maximum batch size of every export. It must be smaller or equal to MaxQueueLength. The default value is 512.
+ /// Gets or sets the maximum batch size of every export. It must be smaller or equal to . The default value is 512.
///
public int MaxExportBatchSize { get; set; } = BatchExportProcessor.DefaultMaxExportBatchSize;
}
diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md
index 3219bd7166c..a11f9108e92 100644
--- a/src/OpenTelemetry/CHANGELOG.md
+++ b/src/OpenTelemetry/CHANGELOG.md
@@ -6,6 +6,42 @@ Notes](../../RELEASENOTES.md).
## Unreleased
+## 1.13.0
+
+Released 2025-Oct-01
+
+* Added a verification to ensure that a `MetricReader` can only be registered
+ to a single `MeterProvider`, as required by the OpenTelemetry specification.
+ ([#6458](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6458))
+
+* Added `FormatMessage` configuration option to self-diagnostics feature. When
+ set to `true` (default is false), log messages will be formatted by replacing
+ placeholders with actual parameter values for improved readability.
+
+ Example `OTEL_DIAGNOSTICS.json`:
+
+ ```json
+ {
+ "LogDirectory": ".",
+ "FileSize": 32768,
+ "LogLevel": "Warning",
+ "FormatMessage": true
+ }
+ ```
+
+* Fixed parsing of `OTEL_TRACES_SAMPLER_ARG` decimal values to always use `.`
+ as the delimiter when using the `traceidratio` sampler, preventing
+ locale-specific parsing issues.
+ ([#6444](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6444))
+
+## 1.12.0
+
+Released 2025-Apr-29
+
+## 1.11.2
+
+Released 2025-Mar-04
+
## 1.11.1
Released 2025-Jan-22
diff --git a/src/OpenTelemetry/CompositeProcessor.cs b/src/OpenTelemetry/CompositeProcessor.cs
index d59936ee87e..df484950e55 100644
--- a/src/OpenTelemetry/CompositeProcessor.cs
+++ b/src/OpenTelemetry/CompositeProcessor.cs
@@ -24,10 +24,12 @@ public CompositeProcessor(IEnumerable> processors)
{
Guard.ThrowIfNull(processors);
+#pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1
using var iter = processors.GetEnumerator();
+#pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1
if (!iter.MoveNext())
{
- throw new ArgumentException($"'{iter}' is null or empty", nameof(iter));
+ throw new ArgumentException($"'{iter}' is null or empty", nameof(processors));
}
this.Head = new DoublyLinkedListNode(iter.Current);
diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
index 88312e3a140..164b1450e5f 100644
--- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
+++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
@@ -16,7 +16,7 @@ namespace OpenTelemetry.Internal;
[EventSource(Name = "OpenTelemetry-Sdk")]
internal sealed class OpenTelemetrySdkEventSource : EventSource, IConfigurationExtensionsLogger
{
- public static OpenTelemetrySdkEventSource Log = new();
+ public static readonly OpenTelemetrySdkEventSource Log = new();
#if DEBUG
public static OpenTelemetryEventListener Listener = new();
#endif
@@ -416,7 +416,7 @@ protected override void OnEventWritten(EventWrittenEventArgs e)
string? message;
if (e.Message != null && e.Payload != null && e.Payload.Count > 0)
{
- message = string.Format(e.Message, e.Payload.ToArray());
+ message = string.Format(System.Globalization.CultureInfo.CurrentCulture, e.Message, e.Payload.ToArray());
}
else
{
diff --git a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs
index 6832ead4946..13ba5fc4df7 100644
--- a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs
+++ b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs
@@ -28,6 +28,9 @@ internal sealed class SelfDiagnosticsConfigParser
private static readonly Regex LogLevelRegex = new(
@"""LogLevel""\s*:\s*""(?.*?)""", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ private static readonly Regex FormatMessageRegex = new(
+ @"""FormatMessage""\s*:\s*(?:""(?.*?)""|(?true|false))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
// This class is called in SelfDiagnosticsConfigRefresher.UpdateMemoryMappedFileFromConfiguration
// in both main thread and the worker thread.
// In theory the variable won't be access at the same time because worker thread first Task.Delay for a few seconds.
@@ -36,11 +39,13 @@ internal sealed class SelfDiagnosticsConfigParser
public bool TryGetConfiguration(
[NotNullWhen(true)] out string? logDirectory,
out int fileSizeInKB,
- out EventLevel logLevel)
+ out EventLevel logLevel,
+ out bool formatMessage)
{
logDirectory = null;
fileSizeInKB = 0;
logLevel = EventLevel.LogAlways;
+ formatMessage = false;
try
{
var configFilePath = ConfigFileName;
@@ -107,6 +112,9 @@ public bool TryGetConfiguration(
return false;
}
+ // FormatMessage is optional, defaults to false
+ _ = TryParseFormatMessage(configJson, out formatMessage);
+
return Enum.TryParse(logLevelString, out logLevel);
}
catch (Exception)
@@ -141,4 +149,17 @@ internal static bool TryParseLogLevel(
logLevel = logLevelResult.Groups["LogLevel"].Value;
return logLevelResult.Success && !string.IsNullOrWhiteSpace(logLevel);
}
+
+ internal static bool TryParseFormatMessage(string configJson, out bool formatMessage)
+ {
+ formatMessage = false;
+ var formatMessageResult = FormatMessageRegex.Match(configJson);
+ if (formatMessageResult.Success)
+ {
+ var formatMessageValue = formatMessageResult.Groups["FormatMessage"].Value;
+ return bool.TryParse(formatMessageValue, out formatMessage);
+ }
+
+ return true;
+ }
}
diff --git a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs
index 3d14dc16e0b..5f515e70ca1 100644
--- a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs
+++ b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs
@@ -35,12 +35,15 @@ internal class SelfDiagnosticsConfigRefresher : IDisposable
// Once the configuration file is valid, an eventListener object will be created.
private SelfDiagnosticsEventListener? eventListener;
+#pragma warning disable CA2213 // Disposable fields should be disposed
private volatile FileStream? underlyingFileStreamForMemoryMappedFile;
private volatile MemoryMappedFile? memoryMappedFile;
+#pragma warning restore CA2213 // Disposable fields should be disposed
private string? logDirectory; // Log directory for log files
private int logFileSize; // Log file size in bytes
private long logFilePosition; // The logger will write into the byte at this position
private EventLevel logEventLevel = (EventLevel)(-1);
+ private bool formatMessage;
public SelfDiagnosticsConfigRefresher()
{
@@ -136,25 +139,27 @@ private async Task Worker(CancellationToken cancellationToken)
private void UpdateMemoryMappedFileFromConfiguration()
{
- if (this.configParser.TryGetConfiguration(out string? newLogDirectory, out int fileSizeInKB, out EventLevel newEventLevel))
+ if (this.configParser.TryGetConfiguration(out string? newLogDirectory, out int fileSizeInKB, out EventLevel newEventLevel, out bool formatMessage))
{
int newFileSize = fileSizeInKB * 1024;
- if (!newLogDirectory.Equals(this.logDirectory) || this.logFileSize != newFileSize)
+ if (!newLogDirectory.Equals(this.logDirectory, StringComparison.Ordinal) || this.logFileSize != newFileSize)
{
this.CloseLogFile();
this.OpenLogFile(newLogDirectory, newFileSize);
}
- if (!newEventLevel.Equals(this.logEventLevel))
+ if (!newEventLevel.Equals(this.logEventLevel) || this.formatMessage != formatMessage)
{
if (this.eventListener != null)
{
this.eventListener.Dispose();
}
- this.eventListener = new SelfDiagnosticsEventListener(newEventLevel, this);
+ this.eventListener = new SelfDiagnosticsEventListener(newEventLevel, this, formatMessage);
this.logEventLevel = newEventLevel;
}
+
+ this.formatMessage = formatMessage;
}
else
{
@@ -194,7 +199,11 @@ private void OpenLogFile(string newLogDirectory, int newFileSize)
{
Directory.CreateDirectory(newLogDirectory);
var fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule?.FileName ?? "OpenTelemetrySdk") + "."
+#if NET
+ + Environment.ProcessId + ".log";
+#else
+ Process.GetCurrentProcess().Id + ".log";
+#endif
var filePath = Path.Combine(newLogDirectory, fileName);
// Because the API [MemoryMappedFile.CreateFromFile][1](the string version) behaves differently on
@@ -255,10 +264,8 @@ private void Dispose(bool disposing)
}
// Dispose EventListener before files, because EventListener writes to files.
- if (this.eventListener != null)
- {
- this.eventListener.Dispose();
- }
+ this.eventListener?.Dispose();
+ this.eventListener = null;
// Ensure worker thread properly finishes.
// Or it might have created another MemoryMappedFile in that thread
diff --git a/src/OpenTelemetry/Internal/SelfDiagnosticsEventListener.cs b/src/OpenTelemetry/Internal/SelfDiagnosticsEventListener.cs
index 2ace3eb5526..cd5bed688bf 100644
--- a/src/OpenTelemetry/Internal/SelfDiagnosticsEventListener.cs
+++ b/src/OpenTelemetry/Internal/SelfDiagnosticsEventListener.cs
@@ -19,17 +19,19 @@ internal sealed class SelfDiagnosticsEventListener : EventListener
private readonly Lock lockObj = new();
private readonly EventLevel logLevel;
private readonly SelfDiagnosticsConfigRefresher configRefresher;
+ private readonly bool formatMessage;
private readonly ThreadLocal writeBuffer = new(() => null);
- private readonly List? eventSourcesBeforeConstructor = new();
+ private readonly List? eventSourcesBeforeConstructor = [];
- private bool disposedValue = false;
+ private bool disposedValue;
- public SelfDiagnosticsEventListener(EventLevel logLevel, SelfDiagnosticsConfigRefresher configRefresher)
+ public SelfDiagnosticsEventListener(EventLevel logLevel, SelfDiagnosticsConfigRefresher configRefresher, bool formatMessage = false)
{
Guard.ThrowIfNull(configRefresher);
this.logLevel = logLevel;
this.configRefresher = configRefresher;
+ this.formatMessage = formatMessage;
List eventSources;
lock (this.lockObj)
@@ -111,61 +113,6 @@ internal static int EncodeInBuffer(string? str, bool isParameter, byte[] buffer,
return position;
}
- internal void WriteEvent(string? eventMessage, ReadOnlyCollection