[
  {
    "path": ".editorconfig",
    "content": "# https://editorconfig.org/\n\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nend_of_line = lf\ncharset = utf-8\n\n[*.conf]\nindent_size = 2\n\n[*.md]\n#inside code block, indentation could be anything\nindent_size = unset\n\n[*.py]\nindent_size = 4\n# 88 is the default for black formatter\n# 79 is PEP8's recommendation\n# 119 is django's recommendation\nmax_line_length = 88\n\n[*.rs]\n# https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md\nindent_size = 4\n# officially the limit is 100, but we have long url (unsplittable) in comment\nmax_line_length = 200\n\n[{*.bazel,*.bzl,BUILD,WORKSPACE}]\nindent_size = 4\n\n[*.java]\n# try to align with https://github.com/diffplug/spotless (https://github.com/google/google-java-format)\nindent_size = 4\nmax_line_length = 100\n\n# The JSON files contain newlines inconsistently\n[*.json]\ninsert_final_newline = unset\n\n[**/vendor/**]\nindent_style = unset\nindent_size = unset\ninsert_final_newline = unset\n\n# Minified JavaScript files shouldn't be changed\n[**.min.js]\nindent_style = unset\nindent_size = unset\ninsert_final_newline = unset\n\n# Makefiles always use tabs for indentation\n[Makefile]\nindent_style = tab\nindent_size = 4\n\n[justfile]\nindent_style = space\nindent_size = 4\n\n# Batch files use tabs for indentation\n[*.bat]\nindent_style = tab\nindent_size = 4\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\n# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\ngithub: [davidB]\n# patreon: # Replace with a single Patreon username\n# open_collective: # Replace with a single Open Collective username\n# ko_fi: # Replace with a single Ko-fi username\n# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\n# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\n# liberapay: # Replace with a single Liberapay username\n# issuehunt: # Replace with a single IssueHunt username\n# otechie: # Replace with a single Otechie username\n# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\n# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "---\nname: ci\n\non:\n  pull_request:\n    branches-ignore:\n      - \"release-plz-*\"\n  push:\n    branches:\n      - main\n      - master\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n\njobs:\n  tests:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os:\n          - ubuntu-latest\n          # - macos-latest\n    env:\n      CARGO_TERM_COLOR: always\n      RUST_BACKTRACE: full\n      SCCACHE_GHA_ENABLED: \"true\"\n      RUSTC_WRAPPER: \"sccache\"\n    steps:\n      - uses: actions/checkout@v6\n      - uses: mozilla-actions/sccache-action@v0.0.9\n      - uses: jdx/mise-action@v4\n        with:\n          # version 2025.5.11, a symlink is created for rust setup\n          # without cache, missing components are installed\n          # with cache, nothing is installed, but as rust tool is symlinked, it is not cached => missing components failure\n          cache: false\n          cache_save: false\n          experimental: true\n      - run: mise run --jobs 1 ci\n      #- run: mise run test-each-feature\n"
  },
  {
    "path": ".github/workflows/mega-linter.yml",
    "content": "# MegaLinter GitHub Action configuration file\n# More info at https://megalinter.io\n---\nname: MegaLinter\n\n# Trigger mega-linter at every push. Action will also be visible from Pull\n# Requests to main\non:\n  push:\n    branches: [main]\n  pull_request:\n  workflow_dispatch:\n\n# Comment env block if you do not want to apply fixes\nenv:\n  # Apply linter fixes configuration\n  #\n  # When active, APPLY_FIXES must also be defined as environment variable\n  # (in github/workflows/mega-linter.yml or other CI tool)\n  APPLY_FIXES: all\n\n  # Decide which event triggers application of fixes in a commit or a PR\n  # (pull_request, push, all)\n  APPLY_FIXES_EVENT: pull_request\n\n  # If APPLY_FIXES is used, defines if the fixes are directly committed (commit)\n  # or posted in a PR (pull_request)\n  APPLY_FIXES_MODE: commit\n\nconcurrency:\n  group: ${{ github.ref }}-${{ github.workflow }}\n  cancel-in-progress: true\n\n# Give the default GITHUB_TOKEN write permission to commit and push, comment\n# issues & post new PR; remove the ones you do not need\npermissions:\n  contents: write\n  issues: write\n  pull-requests: write\n\njobs:\n  megalinter:\n    name: MegaLinter\n    runs-on: ubuntu-latest\n\n    steps:\n      # Git Checkout\n      - name: Checkout Code\n        uses: actions/checkout@v6\n        with:\n          token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}\n\n          # If you use VALIDATE_ALL_CODEBASE = true, you can remove this line to\n          # improve performance\n          fetch-depth: 0\n\n      # MegaLinter\n      - name: MegaLinter\n\n        # You can override MegaLinter flavor used to have faster performances\n        # More info at https://megalinter.io/flavors/\n        uses: oxsecurity/megalinter/flavors/documentation@v9\n\n        id: ml\n\n        # All available variables are described in documentation\n        # https://megalinter.io/configuration/\n        env:\n          # Validates all source when push on main, else just the git diff with\n          # main. Override with true if you always want to lint all sources\n          #\n          # To validate the entire codebase, set to:\n          # VALIDATE_ALL_CODEBASE: true\n          #\n          # To validate only diff with main, set to:\n          # VALIDATE_ALL_CODEBASE: >-\n          #   ${{\n          #     github.event_name == 'push' &&\n          #     contains(fromJSON('[\"refs/heads/main\", \"refs/heads/master\"]'), github.ref)\n          #   }}\n          VALIDATE_ALL_CODEBASE: >-\n            ${{\n              github.event_name == 'push' &&\n              contains(fromJSON('[\"refs/heads/main\", \"refs/heads/master\"]'), github.ref)\n            }}\n\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n          # ADD YOUR CUSTOM ENV VARIABLES HERE OR DEFINE THEM IN A FILE\n          # .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY\n\n          # Uncomment to disable copy-paste and spell checks\n          # DISABLE: COPYPASTE,SPELL\n\n      # Upload MegaLinter artifacts\n      - name: Archive production artifacts\n        uses: actions/upload-artifact@v7\n        if: success() || failure()\n        with:\n          name: MegaLinter reports\n          path: |\n            megalinter-reports\n            mega-linter.log\n\n      # Set APPLY_FIXES_IF var for use in future steps\n      - name: Set APPLY_FIXES_IF var\n        run: |\n          printf 'APPLY_FIXES_IF=%s\\n' \"${{\n            steps.ml.outputs.has_updated_sources == 1 &&\n            (\n              env.APPLY_FIXES_EVENT == 'all' ||\n              env.APPLY_FIXES_EVENT == github.event_name\n            ) &&\n            (\n              github.event_name == 'push' ||\n              github.event.pull_request.head.repo.full_name == github.repository\n            )\n          }}\" >> \"${GITHUB_ENV}\"\n\n      # Set APPLY_FIXES_IF_* vars for use in future steps\n      - name: Set APPLY_FIXES_IF_* vars\n        run: |\n          printf 'APPLY_FIXES_IF_PR=%s\\n' \"${{\n            env.APPLY_FIXES_IF == 'true' &&\n            env.APPLY_FIXES_MODE == 'pull_request'\n          }}\" >> \"${GITHUB_ENV}\"\n          printf 'APPLY_FIXES_IF_COMMIT=%s\\n' \"${{\n            env.APPLY_FIXES_IF == 'true' &&\n            env.APPLY_FIXES_MODE == 'commit' &&\n            (!contains(fromJSON('[\"refs/heads/main\", \"refs/heads/master\"]'), github.ref))\n          }}\" >> \"${GITHUB_ENV}\"\n\n      # Create pull request if applicable\n      # (for now works only on PR from same repository, not from forks)\n      - name: Create Pull Request with applied fixes\n        uses: peter-evans/create-pull-request@v8\n        id: cpr\n        if: env.APPLY_FIXES_IF_PR == 'true'\n        with:\n          token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}\n          commit-message: \"[MegaLinter] Apply linters automatic fixes\"\n          title: \"[MegaLinter] Apply linters automatic fixes\"\n          labels: bot\n\n      - name: Create PR output\n        if: env.APPLY_FIXES_IF_PR == 'true'\n        run: |\n          echo \"PR Number - ${{ steps.cpr.outputs.pull-request-number }}\"\n          echo \"PR URL - ${{ steps.cpr.outputs.pull-request-url }}\"\n\n      # Push new commit if applicable\n      # (for now works only on PR from same repository, not from forks)\n      - name: Prepare commit\n        if: env.APPLY_FIXES_IF_COMMIT == 'true'\n        run: sudo chown -Rc $UID .git/\n\n      - name: Commit and push applied linter fixes\n        uses: stefanzweifel/git-auto-commit-action@v7\n        if: env.APPLY_FIXES_IF_COMMIT == 'true'\n        with:\n          branch: >-\n            ${{\n              github.event.pull_request.head.ref ||\n              github.head_ref ||\n              github.ref\n            }}\n          commit_message: \"[MegaLinter] Apply linters fixes\"\n          commit_user_name: megalinter-bot\n          commit_user_email: nicolas.vuillamy@ox.security\n"
  },
  {
    "path": ".github/workflows/release-plz.yml",
    "content": "name: release-plz\n\npermissions:\n  pull-requests: write\n  contents: write\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n\nconcurrency:\n  group: ${{ github.ref }}-${{ github.workflow }}\n  cancel-in-progress: true\n\njobs:\n  release-plz:\n    name: Release-plz\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run release-plz\n        uses: MarcoIeni/release-plz-action@v0.5\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/git,bazel,vim,emacs,visualstudiocode,jetbrains+all,helm,rust\n# Edit at https://www.toptal.com/developers/gitignore?templates=git,bazel,vim,emacs,visualstudiocode,jetbrains+all,helm,rust\n\n### Bazel ###\n# gitignore template for Bazel build system\n# website: https://bazel.build/\n\n# Ignore all bazel-* symlinks. There is no full list since this can change\n# based on the name of the directory bazel is cloned into.\n/bazel-*\n\n# Directories for the Bazel IntelliJ plugin containing the generated\n# IntelliJ project files and plugin configuration. Seperate directories are\n# for the IntelliJ, Android Studio and CLion versions of the plugin.\n/.ijwb/\n/.aswb/\n/.clwb/\n\n### Emacs ###\n# -*- mode: gitignore; -*-\n*~\n\\#*\\#\n/.emacs.desktop\n/.emacs.desktop.lock\n*.elc\nauto-save-list\ntramp\n.\\#*\n\n# Org-mode\n.org-id-locations\n*_archive\n\n# flymake-mode\n*_flymake.*\n\n# eshell files\n/eshell/history\n/eshell/lastdir\n\n# elpa packages\n/elpa/\n\n# reftex files\n*.rel\n\n# AUCTeX auto folder\n/auto/\n\n# cask packages\n.cask/\ndist/\n\n# Flycheck\nflycheck_*.el\n\n# server auth directory\n/server/\n\n# projectiles files\n.projectile\n\n# directory configuration\n.dir-locals.el\n\n# network security\n/network-security.data\n\n### Git ###\n# Created by git for backups. To disable backups in Git:\n# $ git config --global mergetool.keepBackup false\n*.orig\n\n# Created by git when using merge tools for conflicts\n*.BACKUP.*\n*.BASE.*\n*.LOCAL.*\n*.REMOTE.*\n*_BACKUP_*.txt\n*_BASE_*.txt\n*_LOCAL_*.txt\n*_REMOTE_*.txt\n\n### Helm ###\n# Chart dependencies\n**/charts/*.tgz\n\n### JetBrains+all ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/**/workspace.xml\n.idea/**/tasks.xml\n.idea/**/usage.statistics.xml\n.idea/**/dictionaries\n.idea/**/shelf\n\n# AWS User-specific\n.idea/**/aws.xml\n\n# Generated files\n.idea/**/contentModel.xml\n\n# Sensitive or high-churn files\n.idea/**/dataSources/\n.idea/**/dataSources.ids\n.idea/**/dataSources.local.xml\n.idea/**/sqlDataSources.xml\n.idea/**/dynamic.xml\n.idea/**/uiDesigner.xml\n.idea/**/dbnavigator.xml\n\n# Gradle\n.idea/**/gradle.xml\n.idea/**/libraries\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n# *.iml\n# *.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# SonarLint plugin\n.idea/sonarlint/\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser\n\n### JetBrains+all Patch ###\n# Ignore everything but code style settings and run configurations\n# that are supposed to be shared within teams.\n\n.idea/*\n\n!.idea/codeStyles\n!.idea/runConfigurations\n\n### Rust ###\n# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n### Vim ###\n# Swap\n[._]*.s[a-v][a-z]\n!*.svg # comment out if you don't need vector files\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\nSessionx.vim\n\n# Temporary\n.netrwhist\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n# Local History for Visual Studio Code\n.history/\n\n# Built Visual Studio Code Extensions\n*.vsix\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n# Support for Project snippet scope\n\n# End of https://www.toptal.com/developers/gitignore/api/git,bazel,vim,emacs,visualstudiocode,jetbrains+all,helm,rust\n\n.envrc\n\n# ignore downloaded charts\n*.tgz\n*.off\n\n# ignore report from MegaLinter\nreport/\nmegalinter-reports/\n\ntempo-data\n"
  },
  {
    "path": ".mega-linter.yml",
    "content": "# Configuration file for MegaLinter\n# See all available variables at https://megalinter.github.io/configuration/ and in linters documentation\n\nAPPLY_FIXES: all # all, none, or list of linter keys\n# ENABLE: # If you use ENABLE variable, all other languages/formats/tooling-formats will be disabled by default\n# ENABLE_LINTERS: # If you use ENABLE_LINTERS variable, all other linters will be disabled by default\nDISABLE:\n  - COPYPASTE # Comment to enable checks of excessive copy-pastes\n  - SPELL # Comment to enable checks of spelling mistakes\nDISABLE_LINTERS:\n  - MARKDOWN_MARKDOWN_LINK_CHECK\n  - DOCKERFILE_DOCKERFILELINT\n  - RUST_CLIPPY\n  - REPOSITORY_DEVSKIM\n  - REPOSITORY_KICS\n  - REPOSITORY_TRIVY\nSHOW_ELAPSED_TIME: true\nFILEIO_REPORTER: false\n# DISABLE_ERRORS: true # Uncomment if you want MegaLinter to detect errors but not block CI to pass\nFILTER_REGEX_EXCLUDE: \"(\\\\.lock)|(\\\\.ndjson)|(\\\\.pdf)|(\\\\.csv)|(\\\\.zip)|(\\\\.tar)|(\\\\.ipynb)|(license.*)|(LICENSE.*)|(.*CHANGELOG.*)\"\nSPELL_FILTER_REGEX_INCLUDE: '\\\\.md$'\nRUST_CLIPPY_ARGUMENTS: --workspace --all-features --all-targets -- --deny warnings --allow deprecated --allow unknown-lints\n"
  },
  {
    "path": ".mise.toml",
    "content": "[env]\nOTEL_EXPORTER_OTLP_TRACES_ENDPOINT = \"http://127.0.0.1:4317\"\nOTEL_EXPORTER_OTLP_TRACES_PROTOCOL = \"grpc\"\nOTEL_TRACES_SAMPLER = \"always_on\"\n# RUST_LOG = \"warn,otel::setup=debug\"\n\n# [settings]\n# auto_install_disable_tools = [\n#   \"cargo:cargo-deny\",\n#   \"cargo:cargo-hack\",\n#   \"cargo:cargo-insta\",\n#   \"cargo:cargo-nextest\",\n#   \"cargo:cargo-release\",\n#   \"git-cliff\",\n# ]\n\n[tools]\nrust = { version = \"1.91.0\", profile = \"minimal\", components = \"rustfmt,clippy\" }  # the rust tool stack (with cargo, fmt, clippy) to build source\ngrpcurl = \"1.9\"\nprotoc = \"34.1\"\n# grpc-health-probe = \"*\"\n# sccache = \"0.5\"\n# cargo-binstall allow to download (insteal of build) \"cargo:*\"\n# - do not use cargo-binstall\" (it's a special name used by mise)\n# - \"aqua:cargo-bins/cargo-binstall\" allow to download the binary\n\"aqua:cargo-bins/cargo-binstall\" = \"1\"\n\"cargo:cargo-deny\" = \"latest\"\n\"cargo:cargo-nextest\" = \"latest\"\n\"cargo:cargo-insta\" = \"latest\"\n\"cargo:cargo-release\" = \"latest\"\n\"cargo:cargo-hack\" = \"latest\"\n\"git-cliff\" = \"latest\"\n\n[tasks.autofix]\ndescription = \"Automatically fix some linting issues & format code\"\ndepends_post = [\"format\"]\nrun = \"cargo clippy --all-features --fix --allow-dirty\"\n\n[tasks.format]\nalias = \"fmt\"\ndescription = \"Format the code and sort dependencies\"\nrun = [\n  \"cargo fmt\",\n  # \"cargo sort --workspace --grouped\"\n]\n\n[tasks.deny]\ndescription = \"Run cargo deny checks\"\nrun = \"cargo deny check\"\n\n[tasks.check]\ndescription = \"Check code with all feature combinations\"\nrun = \"cargo hack check --each-feature --no-dev-deps\"\nwait_for = [\"test\", \"lint\"]\n\n[tasks.lint]\ndescription = \"Lint the rust code\"\nrun = [\n  \"cargo fmt --all -- --check\",\n  \"cargo clippy --workspace --all-features --all-targets -- --deny warnings --allow deprecated --allow unknown-lints\",\n]\nwait_for = [\"deny\"]\n\n[tasks.megalinter]\ndescription = \"Run megalinter in container\"\nrun = 'docker run --pull always --rm -it -v \"$PWD:/tmp/lint:rw\" \"oxsecurity/megalinter-documentation:v8\"'\n\n[tasks.test]\ndescription = \"Launch tests\"\nrun = [\n  \"cargo nextest run\",\n  \"cargo test --doc\",\n]\nwait_for = [\"lint\"]\n\n[tasks.\"test:review\"]\ndescription = \"Launch snapshot test and review result (accept or reject)\"\nrun = [\n  \"cargo insta review\",\n]\nwait_for = [\"lint\"]\n\n[tasks.test-each-feature]\ndescription = \"Test each feature separately\"\nrun = \"cargo hack test --each-feature -- --test-threads=1\"\n\n[tasks.set-version]\ndescription = \"Set version across all workspace crates (Usage: mise run set-version <version>)\"\nrun = '''\n#!/usr/bin/env bash\nversion=\"{{arg(name=\"version\")}}\"\nsed -i \"s/^version = .*/version = \\\"$version\\\"/\" Cargo.toml\nrelease-plz set-version axum-tracing-opentelemetry@\"$version\"\nrelease-plz set-version fake-opentelemetry-collector@\"$version\"\nrelease-plz set-version init-tracing-opentelemetry@\"$version\"\n# release-plz set-version testing-tracing-opentelemetry@\"$version\"\nrelease-plz set-version tonic-tracing-opentelemetry@\"$version\"\nrelease-plz set-version tracing-opentelemetry-instrumentation-sdk@\"$version\"\n'''\n\n[tasks.run-otel-desktop-viewer]\ndescription = \"Run otel-desktop-viewer as receiver and viewer of otel trace\"\nrun = [\n  \"# Viewer: open http://localhost:8000\",\n  \"docker run -p 8000:8000 -p 4317:4317 -p 4318:4318 ghcr.io/ctrlspice/otel-desktop-viewer:latest-amd64\",\n]\n\n[tasks.run-jaeger]\ndescription = \"Run Jaeger all-in-one container\"\nrun = [\n  \"# Viewer: open http://localhost:16686\",\n  '''\n  docker run --rm --name jaeger \\\n    -p 16686:16686 \\\n    -p 4317:4317 \\\n    -p 4318:4318 \\\n    -p 5778:5778 \\\n    -p 9411:9411 \\\n    cr.jaegertracing.io/jaegertracing/jaeger:latest\n  '''\n]\n\n[tasks.run-example-grpc-server]\ndescription = \"Run gRPC server example\"\nrun = \"cd examples/grpc && OTEL_SERVICE_NAME=grpc-server cargo run --bin server\"\n\n[tasks.run-example-grpc-client]\ndescription = \"Run gRPC client example\"\nrun = '''\ngrpcurl -plaintext 127.0.0.1:50051 list\ncd examples/grpc && OTEL_SERVICE_NAME=grpc-client cargo run --bin client\n'''\n\n[tasks.run-example-axum-otlp-server]\ndescription = \"Run axum-otlp server example\"\nrun = \"cd examples/axum-otlp && OTEL_SERVICE_NAME=axum-otlp-4317 cargo run\"\n\n[tasks.run-example-axum-otlp-server-http]\ndescription = \"Run axum-otlp server example over HTTP\"\nrun = 'cd examples/axum-otlp && OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://localhost:4318/v1/traces\" OTEL_SERVICE_NAME=axum-otlp-4318 cargo run --features otlp-over-http'\n\n[tasks.run-example-http-server]\ndescription = \"Run HTTP server example (alias for axum-otlp)\"\ndepends = [\"run-example-axum-otlp-server\"]\n\n[tasks.run-example-http-client]\ndescription = \"Run HTTP client example\"\nrun = '''\n# curl -i http://127.0.0.1:3003/health\ncurl -i http://127.0.0.1:3003/\n'''\n\n[tasks.run-example-load]\ndescription = \"Run load test example\"\nrun = \"cd examples/load && cargo run --release 2>/dev/null\"\n\n[tasks.ci]\ndepends = [\"check\", \"lint\", \"test\", \"deny\"]\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\n    \"insta\",\n    \"opentelemetry\",\n    \"OTEL\",\n    \"OTLP\",\n    \"sdktrace\",\n    \"semcov\"\n  ],\n  \"rust-analyzer.linkedProjects\": [\n    \"./Cargo.toml\",\n    \"./axum-tracing-opentelemetry/Cargo.toml\",\n    \"./axum-tracing-opentelemetry/Cargo.toml\"\n  ]\n}\n"
  },
  {
    "path": ".yamllint.yml",
    "content": "###########################################\n# These are the rules used for            #\n# linting all the yaml files in the stack #\n# NOTE:                                   #\n# You can disable line with:              #\n# # yamllint disable-line                 #\n###########################################\nextends: default\nrules:\n  document-start: disable\n  new-lines:\n    level: warning\n    type: unix\n  line-length:\n    max: 800\n  comments:\n    min-spaces-from-content: 1 # Used to follow prettier standard: https://github.com/prettier/prettier/pull/10926\n  indentation:\n    indent-sequences: consistent\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "<!-- markdownlint-disable MD024-->\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n\n### Added\n\n### Changed\n\n- ⬆️ upgrade to opentelemetry 0.24 (and related dependencies)\n\n\n### Fixed\n\n\n## [0.17.0] - 2024-02-11\n\n### Added\n\n- ✨ add support for logfmt into `tracing_subscriber_ext::init_subscribers()` ([580d709](580d709161554b5888697604a6ec125a69503b09))\n\n### Changed\n\n- 📝 update CHANGELOG ([d3609ac](d3609ac2cc699d3a24fbf89754053cc8e938e3bf))\n- 📝 update link to homepage ([3e081fb](3e081fbd3a410fb9abfbdc0cc7a5a2953fee823a))\n- 💄 fix display of OTEL_TRACES_SAMPLER ([62d9c2a](62d9c2a7af020015949e1eaf308d365124aad43b))\n\n### Fixed\n\n- 🐛 on grpc when no status code into header, fallback to OK (previously Unkown) ([f1a23c4](f1a23c4cdeba8abcea598a2d4305dbe6e1a10edf))\n\n## [0.16.0] - 2023-12-30\n\n### Added\n\n- ✨ add support for OTLP headers from environment (#110) ([ccd123b](ccd123b6d7de9c1f10d3d861cb8494db9ed201ee))\n\n### Changed\n\n- 📝 update CHANGELOG ([319b1eb](319b1eb17cc8876d3b7f999a4e1d5b4f534d2816))\n- 📝 Update link to changelog, remove homepage, ... ([7f38094](7f380949f73c76a315779db727b75be32211804d))\n- ➖ remove dependency to opentelemetry-http ([e049fb0](e049fb0e0c67140b3252bf465aa3c74e6838400d))\n- ⬆️ upgrade dependencies for axum-0.7 ([d4ad2d3](d4ad2d31bf8787b8c99332f6b8a7e44e34088886))\n- 📝 update example in doc ([b74c686](b74c68604ab359b19d4e43da1a3b0514e1ec2e68))\n\n### Fixed\n\n- 🐛 fix compilation & linter ([24d1eca](24d1eca18a2f85bd2fda98389583684a89d42e7c))\n\n## [0.15.0] - 2023-11-25\n\n### Added\n\n- ✨ add attribute `rpc.grpc.status_code` ([d885954](d8859542f80cf0df365ee18c3fcce654e2e1a843))\n\n### Changed\n\n- ⬆️ upgrade to openteletry 0.21 (and related dependencies) ([21ceb34](21ceb3450973b288743c9fc026cc45072364bb5e))\n\n### Fixed\n\n- 🐛 attribute `http.response.status_code` should of type `int` ([6ff9209](6ff9209175101ed767fd5eee0f5a33f663755dce))\n\n## [0.14.0] - 2023-09-04\n\n### Added\n\n- ✨ enable simple basic grpc tls endpoint (#85) ([ecf4f9d](ecf4f9decce5e14766e6e7c24138bcc3519cd540))\n\n### Changed\n\n- ✏️ fix typo in homepage of init-tracing-opentelemetry ([9cfbaff](9cfbaff8f344e3ba918c7b0fa2587d0d18945172))\n- ⬆️ bump tracing-opentelemetry from 0.20 to 0.21 ([6763c41](6763c41ba06e34fe1382e1f797c539e57cbe9cf5))\n- ⬆️ bump tonic from 0.9 to 0.10 in tonic-tracing-opentelemetrty ([f33bfe6](f33bfe6f77fd1013497d79f147ce2732d3f4e3ea))\n\n## [0.13.0] - 2023-08-06\n\n### Added\n\n- Feat: add span.type=web on spans ([d76017f](d76017f797b5b9cf2a649824aaea07c81cf84dcf))\n- Feat: add span_type enum and documentation ([4871359](487135955342241b2633968c2162415159b9cdab))\n\n### Changed\n\n- ⬆️ upgrade to opentelemetry 0.20 (and related dependencies) ([8b8281e](8b8281ee5e938143379db7d5ef645a830ba87c51))\n\n## [0.12.0] - 2023-07-02\n\n### Changed\n\n- 📝 update README ([400adeb](400adeb7b1105f0c197a29b6a27ec35fe4b1f722))\n\n## [0.12.0-alpha.2] - 2023-06-28\n\n### Added\n\n- 💥 use `otel::tracing` as target for trace instead on the name of the crate ([1fda7c3](1fda7c3d566d4a622710116616d9d28680b7b475))\n- ✨  introduce new crate `tracing-opentelemetry-instrumentation-sdk` ([51c45ae](51c45ae5f892e0efbea0ce957d3f3a7524bfe927))\n- ✨ grpc server layer can use a filter function to not create trace for some endpoint ([2f3ca50](2f3ca5045ab43fcd5c2f3985f9117c9940d5f3ae))\n- 💥 rewrite axum-tracing-opentelemetry ([661b891](661b8917d61b52e8d682863e75bece9ad76e9f9b))\n\n### Changed\n\n- ⚡️ tag as `inline` some helpers function ([753b1a7](753b1a72ece620a46461f7860360ebc347f518bb))\n\n### Fixed\n\n- 🐛 grpc client set the span context during async children processing ([cec0ce5](cec0ce531fbca3caf371ee290593e9cf5e226bf7))\n- 🐛 grpc server set the span context during async children processing ([83d88e4](83d88e466049c4613cdd10e2d6668cd3a3d0428e))\n\n## [0.12.0-alpha.1] - 2023-06-14\n\n### Added\n\n- ✨ add basic filtering for axum-tracing-opentelemetry ([bb510a3](bb510a32148182090264d5be9d1c9abe21895083))\n\n### Changed\n\n- 📝 add notes about how to release the workspace ([d1abae1](d1abae15855cfb6fb3d058fbb134f31da82018e3))\n\n## [0.12.0-alpha.0] - 2023-06-14\n\n### Added\n\n- ✨ extract `fake-opentelemetry-collector` ([25becbb](25becbb6633336c189cb2ab02ff94f7530e8ac57))\n- ✨ start the tonic-tracing-opentelemetry ([43c179f](43c179f28aa295a81428d2b08ebae83397329943))\n- ✨ start the testing-tracing-opentelemetry ([d7ecb0d](d7ecb0dd416fd4d7d78e51abaa8d10a4c0fbb63a))\n\n### Changed\n\n- ➖ remove more unused dependencies ([46793cf](46793cf708952e21ee316dcedaf1873acf175600))\n\n## [0.11.0] - 2023-06-11\n\n### Added\n\n- ✨ add a mock_collector server to to collect trace ([b36f5b1](b36f5b1557963d5678f8a337e0bf45606fb03dcf))\n\n### Changed\n\n- ⬆️ upgrade opentelemetry to 0.19 (and related dependencies) ([36b52a0](36b52a0bad4babfc8ace5fcaab79e897907890d3))\n- ⬆️ upgrade opentelemetry to 0.19 (and related dependencies)  (2) ([b7a2a0e](b7a2a0ed9990c35424335e6cf71fa7e28ba1e60b))\n- ⬆️ upgrade opentelemetry to 0.19 (and related dependencies)  (3) ([b8719a2](b8719a2912edac8e6556774530fbf99afb82a955))\n\n### Fixed\n\n- 🐛 fix features dependencies ([bdc949d](bdc949d2d0f1eafe0e44ecdbf4607f040150641d))\n- Fix: fallback to req uri path for nested route (we can not get matched router in nested router handler) ([36a4302](36a43025ba54721dbb41306086a9135b80350f6c))\n- 🐛 generate root opentelemetry span with valid spanId ([c5738a6](c5738a6a4586f9cabd66330335c8353b528498ed))\n\n## [0.10.0] - 2023-02-26\n\n### Added\n\n- 💥 default configuration for otlp Sampler is now longer hardcoded to `always_on`, but read environment variables `OTEL_TRACES_SAMPLER`, `OTEL_TRACES_SAMPLER_ARG` ([c20e7c7](c20e7c77bb2da30737f78a48e4a513d6f3117f24))\n- ✨ add a axum layer for gRPC (#36) ([bf7daee](bf7daeeebe1ffd07834388c81349ab7a972abdbe))\n- ✨ log under target `otel::setup` detected configuration by otel setup tools ([6c2f5c1](6c2f5c119bea731cb3de770dabcfe726c8edc227))\n- ✨ provide opinionated `tracing_subscriber_ext` ([53963eb](53963eb1ee543f3b1c0a0a90a9c00a319694f71b))\n\n### Changed\n\n- 📝 add sample to overwrite `otel.name` ([1dae1aa](1dae1aab7edb2b1cc793ab1e609ea6153e73f2d3))\n- 📝 update changelog ([2945358](29453580794da60dacf449676820a8731fd036e9))\n\n## [0.9.0] - 2023-02-05\n\n### Added\n\n- ✨ add `DetectResource` builder to help detection for [Resource Semantic Conventions | OpenTelemetry](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/#semantic-attributes-with-sdk-provided-default-value) ([db7552e](db7552efdc5ea842bc17e19604a46ebe77283d0c))\n\n### Changed\n\n- 📝 add instruction to launch jaeger for local dev ([95411e9](95411e9640fc7a1e4eaf7bc5a6d9d07362cfe752))\n- 📝 improve sample ([1b91fbf](1b91fbf9172578a81598292666fa9bd854a7f4c5))\n\n### Fixed\n\n- 🐛 fix mega-linter.yml ([6494dd6](6494dd6dab76f09e3364b1fe508ee6edae39356b))\n\n## [0.8.2] - 2023-01-30\n\n### Fixed\n\n- 🐛 restore missing line in changelog ([f46c342](f46c3427fa6f31e8aa4e550315160ce2bcbafb1b))\n- 🐛 use correct env variable (OTEL_PROPAGATORS) when setting up propagators ([c2d34eb](c2d34eb54a7672b62aefd3429cc264235c5f952d))\n\n## [0.8.1] - 2023-01-29\n\n### Added\n\n- ✨ add `init_propagator` based on OTEL_PROPAGATORS ([b45b2f3](b45b2f3a39afaf86a589b5cef01e147f11416c3d))\n\n### Changed\n\n- 📝 update documentation & samples about configuration ([75a040d](75a040d08ebd41901a803562b1b8788f9a38e031))\n\n## [0.7.1] - 2023-01-01\n\n### Changed\n\n- 📝 use more OTEL env variable into sample ([048f57c](048f57c668a352739172ffd3af965f263452a4a2))\n\n## [0.7.0] - 2022-12-28\n\n### Added\n\n- ✨ add a layer`response_with_trace_layer` to have `traceparent` injected into response ([368c59d](368c59d0b0a928459b24e21ff26eac337c79a283))\n\n### Changed\n\n- 📝 add compatibility matrix ([9312737](93127375a8393a4b8df9dddbd108f875c1ab9cee))\n- 📝 update changelog ([820ae63](820ae63eedcfaa76d5182c9c628c233a007ce8e0))\n\n## [0.5.2] - 2022-11-06\n\n### Fixed\n\n- Fix: do not populate http.route when not supported by the HTTP server framework ([93cedaa](93cedaa7a94904cff127a966db94b70dd697cc6a))\n\n## [0.5.1] - 2022-11-01\n\n### Added\n\n- :green_heart: add protoc into the CI require by `opentelemetry-proto` ([a1777c6](a1777c631c603074f9928787d156a5d4806bc708))\n\n### Removed\n\n- :rotating_light: remove useless code (after validation that experiment is ok) ([b17d9f0](b17d9f0a54a76ea3b185acbcdb97bfd0efffca98))\n\n## [0.3.0] - 2022-08-04\n\n### Added\n\n- :pencil: add a sample about how to retrieve trace_id ([6dd26ff](6dd26ff288f95b719a42c4c1939596532a3f9e4c))\n\n### Removed\n\n- :heavy_minus_sign: remove unused tansitive dependencies ([bca0c14](bca0c1485ac47ee2756f5dc7963863b2f6d39057))\n\n## [0.2.1] - 2022-06-11\n\n### Added\n\n- :sparkles: add code for opentelemetry_tracing_layer ([9403583](94035838f97aa61ad304791f0f3174e042a566f3))\n- :sparkles: add tools to init tracer and find trace_id ([acb52a3](acb52a3ed98aeed7733ce3dba7aba894122a8949))\n- :pencil: add examples code ([0482b59](0482b59f19af6d0e74082b43c19783e1d08e4c95))\n- :pencil: add missing info for release ([a2f7c09](a2f7c0961366bcfd160ee89360d30c8874cfb6fd))\n\n<!-- generated by git-cliff -->\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to tracing-opentelemetry-instrumentation-sdk\n\nThank you for your interest in contributing! This document provides guidelines and instructions for setting up your development environment and contributing to the project.\n\n## Development Environment Setup\n\n### Prerequisites\n\n1. **Install mise** (if not already installed):\n\n   ```bash\n   # Using the install script\n   curl https://mise.run | sh\n\n   # Or using a package manager\n   # macOS: brew install mise\n   # Arch: pacman -S mise\n   # Ubuntu/Debian: see https://mise.jdx.dev/installing-mise.html\n   ```\n\n2. **Clone the repository**:\n\n   ```bash\n   git clone https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk.git\n   cd tracing-opentelemetry-instrumentation-sdk\n   ```\n\n3. **Install tools and dependencies**:\n\n   ```bash\n   # Install all tools defined in .mise.toml (Rust, protoc, grpcurl, etc.)\n   mise install\n\n   # Activate the environment (or use 'mise use' for shell integration)\n   mise activate\n   ```\n\n### Available Development Tasks\n\nWe use `mise` tasks for all development workflows. Here are the main tasks:\n\n#### Code Quality & Formatting\n\n```bash\n# Format code\nmise run format\n\n# Lint code (clippy + format check)\nmise run lint\n\n# Check code with all feature combinations\nmise run check\n\n# Run cargo deny security checks\nmise run deny\n```\n\n#### Testing\n\n```bash\n# Run tests (using nextest + doctests)\nmise run test\n\n# Test each feature separately (slower but thorough)\nmise run test-each-feature\n```\n\n#### Tool Installation\n\nThese tasks automatically install required tools if not present:\n\n```bash\n# Individual tool installation (usually handled automatically by other tasks)\nmise run install:cargo-hack\nmise run install:cargo-nextest\nmise run install:cargo-insta\nmise run install:cargo-deny\n```\n\n#### Container & Examples\n\n```bash\n# Start Jaeger all-in-one for local development\nmise run run-jaeger\n\n# Run example applications\nmise run run-example-grpc-server     # gRPC server example\nmise run run-example-grpc-client     # gRPC client example\nmise run run-example-axum-otlp-server # Axum HTTP server\nmise run run-example-http-client     # HTTP client test\nmise run run-example-load            # Load testing example\n```\n\n#### Version Management\n\n```bash\n# Set version across all workspace crates\nmise run set-version 0.1.0\n```\n\n### Development Workflow\n\n1. **Setup environment** (first time only):\n\n   ```bash\n   mise install\n   ```\n\n2. **Before making changes**:\n\n   ```bash\n   # Format and check code\n   mise run format\n   mise run lint\n   mise run check\n   ```\n\n3. **While developing**:\n\n   ```bash\n   # Run tests frequently\n   mise run test\n\n   # Test with examples if relevant\n   mise run run-jaeger &  # Start Jaeger in background\n   mise run run-example-axum-otlp-server\n   ```\n\n4. **Before submitting PR**:\n   ```bash\n   # Full validation\n   mise run format\n   mise run lint\n   mise run check\n   mise run test\n   mise run deny\n   ```\n\n### Testing with OpenTelemetry\n\n#### Local Jaeger Setup\n\n```bash\n# Start Jaeger (runs on various ports including 16686 for UI, 4317/4318 for OTLP)\nmise run run-jaeger\n\n# Open Jaeger UI\nopen http://localhost:16686\n```\n\n#### Running Examples\n\n```bash\n# Terminal 1: Start Jaeger\nmise run run-jaeger\n\n# Terminal 2: Start example server\nmise run run-example-axum-otlp-server\n\n# Terminal 3: Send requests and check traces\nmise run run-example-http-client\n# Then check traces in Jaeger UI at http://localhost:16686\n```\n\n### Project Structure\n\nThis workspace contains several crates:\n\n- **`init-tracing-opentelemetry/`**: Helpers to initialize tracing + opentelemetry\n- **`axum-tracing-opentelemetry/`**: Axum middlewares for tracing integration\n- **`tonic-tracing-opentelemetry/`**: Tonic (gRPC) middlewares for tracing\n- **`tracing-opentelemetry-instrumentation-sdk/`**: Core instrumentation SDK\n- **`fake-opentelemetry-collector/`**: Testing utilities and fake collector\n- **`testing-tracing-opentelemetry/`**: Test utilities\n- **`examples/`**: Working examples demonstrating usage\n\n### Code Style & Guidelines\n\n- **Formatting**: We use `rustfmt` with default settings\n- **Linting**: All clippy warnings must be addressed\n- **Testing**: Add tests for new functionality\n- **Documentation**: Update documentation for public APIs\n- **Examples**: Update examples if adding new features\n\n### Continuous Integration\n\nOur CI pipeline runs:\n\n- `mise run check` - Feature compatibility checks\n- `mise run lint` - Code formatting and clippy\n- `mise run test` - Full test suite\n- `mise run deny` - Security and license checks\n\nMake sure all these pass locally before submitting a PR.\n\n### Common Issues & Solutions\n\n#### Tool Installation Issues\n\nIf tools fail to install automatically:\n\n```bash\n# Install cargo-binstall manually\ncurl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash\n\n# Then retry the task\nmise run <task-name>\n```\n\n#### Container Runtime\n\nThe project supports multiple container runtimes (podman, nerdctl, docker). If you have issues:\n\n```bash\n# Make sure one of these is installed and available\nwhich podman || which nerdctl || which docker\n```\n\n#### Environment Variables\n\nKey environment variables (already set in `.mise.toml`):\n\n- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://127.0.0.1:4317\"`\n- `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=\"grpc\"`\n- `OTEL_TRACES_SAMPLER=\"always_on\"`\n\n### Getting Help\n\n- Check existing [issues](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/issues)\n- Review the [examples/](examples/) directory for usage patterns\n- Look at test files for API usage examples\n- Open a new issue for bugs or feature requests\n\n### Submitting Changes\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/my-new-feature`\n3. Make your changes following the guidelines above\n4. Run the full test suite: `mise run lint && mise run test && mise run deny`\n5. Commit with descriptive messages\n6. Push to your fork and submit a pull request\n\nThank you for contributing!\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n  \"axum-tracing-opentelemetry\",\n  \"examples/*\",\n  \"fake-opentelemetry-collector\",\n  \"init-tracing-opentelemetry\",\n  \"testing-tracing-opentelemetry\",\n  \"tonic-tracing-opentelemetry\",\n  \"tracing-opentelemetry-instrumentation-sdk\",\n]\nexclude = [\"target\"]\n\n[workspace.package]\nversion = \"0.32.0\"\nedition = \"2024\"\nrust-version = \"1.91.0\"\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk\"\nrepository = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk\"\nlicense = \"CC0-1.0\"\n\n[workspace.dependencies]\nassert2 = \"0.4\"\naxum = { version = \"0.8\", default-features = false }\nhttp = \"^1\"\nhyper = \"1\"\ninsta = { version = \"^1\", features = [\"redactions\", \"yaml\"] }\nopentelemetry = { version = \"0.31\", default-features = false, features = [\n  \"trace\",\n] }\nopentelemetry-aws = { version = \"0.19\", default-features = false }\nopentelemetry-jaeger-propagator = { version = \"0.31\", default-features = false }\nopentelemetry-otlp = { version = \"0.31\", default-features = false }\nopentelemetry-proto = { version = \"0.31\", default-features = false }\nopentelemetry-resource-detectors = { version = \"0.10\", default-features = false }\nopentelemetry_sdk = { version = \"0.31\", default-features = false, features = [\n  \"rt-tokio\",\n] }\nopentelemetry-semantic-conventions = { version = \"0.31\", default-features = false }\nopentelemetry-stdout = { version = \"0.31\" }\nopentelemetry-zipkin = { version = \"0.31\", default-features = false }\nrstest = \"0.26\"\ntokio = { version = \"1\", default-features = false }\ntokio-stream = { version = \"0.1\", default-features = false }\ntonic = { version = \"0.14\", default-features = false } # should be sync with opentelemetry-proto\ntower = { version = \"0.5\", default-features = false }\ntracing = \"0.1\"\ntracing-opentelemetry = \"0.32\"\n\n[workspace.metadata.release]\npre-release-commit-message = \"🚀 (cargo-release) version {{version}}\"\ntag-prefix = \"\"\ntag-name = \"{{prefix}}{{version}}\"\ntag-message = \"🔖 {{version}}\"\n\n[profile.dev.package.insta]\nopt-level = 3\n\n[profile.dev.package.similar]\nopt-level = 3\n"
  },
  {
    "path": "LICENSE",
    "content": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n    HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n  i. the right to reproduce, adapt, distribute, perform, display,\n     communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n     likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n     subject to the limitations in paragraph 4(a), below;\n  v. rights protecting the extraction, dissemination, use and reuse of data\n     in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n     European Parliament and of the Council of 11 March 1996 on the legal\n     protection of databases, and under any national implementation\n     thereof, including any amended or successor version of such\n     directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n     world based on applicable law or treaty, and any national\n     implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n    warranties of any kind concerning the Work, express, implied,\n    statutory or otherwise, including without limitation warranties of\n    title, merchantability, fitness for a particular purpose, non\n    infringement, or the absence of latent or other defects, accuracy, or\n    the present or absence of errors, whether or not discoverable, all to\n    the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n    that may apply to the Work or any use thereof, including without\n    limitation any person's Copyright and Related Rights in the Work.\n    Further, Affirmer disclaims responsibility for obtaining any necessary\n    consents, permissions or other rights required for any use of the\n    Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n    party to this document and has no duty or obligation with respect to\n    this CC0 or use of the Work.\n"
  },
  {
    "path": "README.md",
    "content": "# tracing-opentelemetry-instrumentation-sdk\n\nA set of rust crates to help working with tracing + opentelemetry\n\n- `init-tracing-opentelemetry`: A set of helpers to initialize (and more) tracing + opentelemetry (compose your own or use opinionated preset)\n- `axum-tracing-opentelemetry`: Middlewares and tools to integrate axum + tracing + opentelemetry.\n- `fake-opentelemetry-collector`: A Fake (basic) opentelemetry collector, useful to test what is collected opentelemetry\n\n## For local dev / demo\n\nTo collect and visualize trace on local, some ofthe simplest solutions:\n\n![screenshot](examples/axum-otlp/Screenshot-20251103_1308.jpg)\n\n### Otel Desktop Viewer\n\n[CtrlSpice/otel-desktop-viewer: desktop-collector](https://github.com/CtrlSpice/otel-desktop-viewer)\n\n```sh\n# also available via `brew install --cask ctrlspice/tap/otel-desktop-viewer`\n# For AMD64 (most common)\n# docker/nerdctl/podman or any container runner\ndocker run -p 8000:8000 -p 4317:4317 -p 4318:4318 ghcr.io/ctrlspice/otel-desktop-viewer:latest-amd64\n\nopen http://localhost:8000\n```\n\n### Jaeger all-in-one\n\n```sh\n# launch Jaeger with OpenTelemetry, Jaeger, Zipking,... mode.\n# see https://www.jaegertracing.io/docs/1.49/getting-started/#all-in-one\n\n# docker/nerdctl/podman or any container runner\ndocker run --rm --name jaeger \\\n  -p 16686:16686 \\\n  -p 4317:4317 \\\n  -p 4318:4318 \\\n  -p 5778:5778 \\\n  -p 9411:9411 \\\n  cr.jaegertracing.io/jaegertracing/jaeger:latest\n\nopen http://localhost:16686\n```\n\nThen :\n\n- setup env variable (or not), (eg see [.envrc](.envrc))\n- launch your server\n- send the request\n- copy trace_id from log (or response header)\n- paste into Jaeger web UI\n\n## To release\n\nUse the github workflow `release-plz`.\n"
  },
  {
    "path": "axum-tracing-opentelemetry/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.30.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.29.0...axum-tracing-opentelemetry-v0.30.0) - 2025-08-25\n\n### <!-- 2 -->Added\n\n- *(axum)* optional extraction of `client.address` (former `client_ip`) from http headers or socket's info\n# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.33.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/releases/tag/axum-tracing-opentelemetry-v0.33.0) - 2026-01-19\n\n### <!-- 2 -->Added\n\n- *(deps)* update to rust 1.87 & edition 2024 ([#317](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/317))\n\n## [0.33.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.32.3...axum-tracing-opentelemetry-v0.33.0) - 2026-01-19\n\n### <!-- 2 -->Added\n\n- *(deps)* update to rust 1.87 & edition 2024 ([#317](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/317))\n\n## [0.32.2](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.32.1...axum-tracing-opentelemetry-v0.32.2) - 2025-11-03\n\n### <!-- 4 -->Changed\n\n- update sample\n\n## [0.32.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.32.0...axum-tracing-opentelemetry-v0.32.1) - 2025-10-14\n\n### Wip\n\n- use `opentelemetry-semantic-conventions` instead of `static &str`\n\n## [0.30.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.30.0...axum-tracing-opentelemetry-v0.30.1) - 2025-09-27\n\n## [0.28.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.28.0...axum-tracing-opentelemetry-v0.28.1) - 2025-06-03\n\n### <!-- 3 -->Removed\n\n- remove deprecated sample from README\n\n## [0.26.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.26.0...axum-tracing-opentelemetry-v0.26.1) - 2025-02-26\n\n### <!-- 3 -->Removed\n\n- *(deps)* remove minor constraint when major > 1\n\n## [0.25.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.24.2...axum-tracing-opentelemetry-v0.25.0) - 2025-01-02\n\n### <!-- 2 -->Added\n\n- *(deps)* update rust crate axum to 0.8 (#197)\n\n## [0.24.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.24.0...axum-tracing-opentelemetry-v0.24.1) - 2024-11-24\n\n### <!-- 1 -->Fixed\n\n- Use guard pattern to allow consumers to ensure final trace is sent ([#185](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/185))\n\n## [0.21.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.19.0...axum-tracing-opentelemetry-v0.21.0) - 2024-08-31\n\n### <!-- 1 -->Fixed\n- 🐛 workaround for a delay, batch,... behavior in otlp exporter and test with fake-opentelemetry-collector (closed too early)\n- 🐛 fix build of contributions (upgrade of opentelemetry, fake collector for logs,...)\n- 🐛  Re-export tracing_level_info feature from axum to sdk ([#147](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/147))\n\n### <!-- 4 -->Changed\n- 💄 update deprecated syntax \"default_features\" in Cargo.toml\n- ⬆️ upgrade to rstest 0.22\n\n## [0.17.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/axum-tracing-opentelemetry-v0.17.0...axum-tracing-opentelemetry-v0.17.1) - 2024-02-24\n\n### Other\n- 👷 tune release-plz\n- ✏️ Fix broken /examples URLs ([#129](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/129))\n"
  },
  {
    "path": "axum-tracing-opentelemetry/Cargo.toml",
    "content": "[package]\nname = \"axum-tracing-opentelemetry\"\ndescription = \"Middlewares and tools to integrate axum + tracing + opentelemetry\"\nreadme = \"README.md\"\nkeywords = [\"axum\", \"tracing\", \"opentelemetry\"]\ncategories = [\n  \"development-tools::debugging\",\n  \"development-tools::profiling\",\n  \"web-programming\",\n]\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/axum-tracing-opentelemetry\"\nrust-version.workspace = true\nedition.workspace = true\nversion = \"0.33.1\"\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\naxum = { workspace = true, features = [\"matched-path\", \"tokio\"] }\nfutures-core = \"0.3\"\nfutures-util = { version = \"0.3\", default-features = false, features = [] }\nhttp = { workspace = true }\nopentelemetry = { workspace = true, features = [\n  \"trace\",\n], default-features = false }\nopentelemetry-semantic-conventions = { workspace = true }\npin-project-lite = \"0.2\"\ntower = { workspace = true }\ntracing = { workspace = true }\ntracing-opentelemetry = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../tracing-opentelemetry-instrumentation-sdk\", features = [\n  \"http\",\n], version = \"0.32\" }\n\n[dev-dependencies]\nfake-opentelemetry-collector = { path = \"../fake-opentelemetry-collector\" }\ntesting-tracing-opentelemetry = { path = \"../testing-tracing-opentelemetry\" }\nassert2 = { workspace = true }\nhyper = { workspace = true }\ninsta = { workspace = true }\nopentelemetry-otlp = { workspace = true, features = [\n  \"http-proto\",\n  \"reqwest-client\",\n  \"reqwest-rustls\",\n] }\nopentelemetry-proto = { workspace = true, features = [\"gen-tonic\"] }\nrstest = { workspace = true }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\ntokio = { workspace = true, features = [\"full\"] }\ntokio-stream = { workspace = true, features = [\"net\"] }\ntracing-subscriber = { version = \"0.3\", default-features = false, features = [\n  \"env-filter\",\n  \"fmt\",\n  \"json\",\n] }\n# need tokio runtime to run smoke tests.\nopentelemetry_sdk = { workspace = true, features = [\n  \"trace\",\n  \"rt-tokio\",\n  \"testing\",\n] }\n\n[features]\n# to use level `info` instead of `trace` to create otel span\ntracing_level_info = [\n  \"tracing-opentelemetry-instrumentation-sdk/tracing_level_info\",\n]\n"
  },
  {
    "path": "axum-tracing-opentelemetry/README.md",
    "content": "# axum-tracing-opentelemetry\n\n[![crates license](https://img.shields.io/crates/l/axum-tracing-opentelemetry.svg)](http://creativecommons.org/publicdomain/zero/1.0/)\n[![crate version](https://img.shields.io/crates/v/axum-tracing-opentelemetry.svg)](https://crates.io/crates/axum-tracing-opentelemetry)\n\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n\nMiddlewares to integrate axum + tracing + opentelemetry.\n\n- Read OpenTelemetry header from incoming request\n- Start a new trace if no trace found in the incoming request\n- Trace is attached into tracing'span\n- OpenTelemetry Span is created on close of the tracing's span (behavior from [tracing-opentelemetry])\n\nFor examples, you can look at the [examples](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/examples/) folder.\n\n```txt\n//...\nuse axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer};\n\n#[tokio::main]\nasync fn main() -> Result<(), axum::BoxError> {\n    // very opinionated init of tracing, look as is source to make your own\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n\n    let app = app();\n    // run it\n    let addr = &\"0.0.0.0:3000\".parse::<SocketAddr>()?;\n    tracing::warn!(\"listening on {}\", addr);\n    let listener = tokio::net::TcpListener::bind(addr).await?;\n    axum::serve(listener, app.into_make_service()).await?;\n    Ok(())\n}\n\nfn app() -> Router {\n    Router::new()\n        .route(\"/\", get(index)) // request processed inside span\n        // include trace context as header into the response\n        .layer(OtelInResponseLayer::default())\n        //start OpenTelemetry trace on incoming request\n        .layer(OtelAxumLayer::default())\n        .route(\"/health\", get(health)) // request processed without span / trace\n}\n```\n\nFor more info about how to initialize, you can look at crate [`init-tracing-opentelemetry`] or [`tracing-opentelemetry`].\n\n![screenshot](../examples/axum-otlp/Screenshot-20251103_1308.jpg)\n\n## Changelog - History\n\n[CHANGELOG.md](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/main/CHANGELOG.md)\n\n[`tracing-opentelemetry`]: https://crates.io/crates/tracing-opentelemetry\n[`init-tracing-opentelemetry`]: https://crates.io/crates/init-tracing-opentelemetry\n"
  },
  {
    "path": "axum-tracing-opentelemetry/src/lib.rs",
    "content": "//#![warn(missing_docs)]\n#![forbid(unsafe_code)]\n#![warn(clippy::perf)]\n#![warn(clippy::pedantic)]\n#![allow(clippy::module_name_repetitions)]\n#![doc = include_str!(\"../README.md\")]\n\n#[allow(deprecated)]\npub mod middleware;\n\n/// for basic backward compatibility and transition\n#[allow(deprecated)]\npub use self::middleware::opentelemetry_tracing_layer;\n/// for basic backward compatibility and transition\n#[allow(deprecated)]\npub use self::middleware::response_with_trace_layer;\n\n// reexport tracing_opentelemetry_instrumentation_sdk crate\npub use tracing_opentelemetry_instrumentation_sdk;\n"
  },
  {
    "path": "axum-tracing-opentelemetry/src/middleware/mod.rs",
    "content": "mod response_injector;\nmod trace_extractor;\n\npub use response_injector::*;\npub use trace_extractor::*;\n"
  },
  {
    "path": "axum-tracing-opentelemetry/src/middleware/response_injector.rs",
    "content": "use futures_core::future::BoxFuture;\nuse http::{Request, Response};\nuse std::task::{Context, Poll};\nuse tower::{Layer, Service};\nuse tracing_opentelemetry_instrumentation_sdk as otel;\nuse tracing_opentelemetry_instrumentation_sdk::http as otel_http;\n\n#[deprecated(\n    since = \"0.12.0\",\n    note = \"keep for transition, replaced by OtelInResponseLayer\"\n)]\n#[must_use]\npub fn response_with_trace_layer() -> OtelInResponseLayer {\n    OtelInResponseLayer {}\n}\n\n#[derive(Default, Debug, Clone)]\npub struct OtelInResponseLayer;\n\nimpl<S> Layer<S> for OtelInResponseLayer {\n    type Service = OtelInResponseService<S>;\n\n    fn layer(&self, inner: S) -> Self::Service {\n        OtelInResponseService { inner }\n    }\n}\n\n#[derive(Default, Debug, Clone)]\npub struct OtelInResponseService<S> {\n    inner: S,\n}\n\nimpl<S, B, B2> Service<Request<B>> for OtelInResponseService<S>\nwhere\n    S: Service<Request<B>, Response = Response<B2>> + Send + 'static,\n    S::Future: Send + 'static,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    // `BoxFuture` is a type alias for `Pin<Box<dyn Future + Send + 'a>>`\n    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx)\n    }\n\n    #[allow(unused_mut)]\n    fn call(&mut self, mut request: Request<B>) -> Self::Future {\n        let future = self.inner.call(request);\n\n        Box::pin(async move {\n            let mut response = future.await?;\n            // inject the trace context into the response (optional but useful for debugging and client)\n            otel_http::inject_context(&otel::find_current_context(), response.headers_mut());\n            Ok(response)\n        })\n    }\n}\n"
  },
  {
    "path": "axum-tracing-opentelemetry/src/middleware/trace_extractor.rs",
    "content": "//\n//! `OpenTelemetry` tracing middleware.\n//!\n//! This returns a [`OtelAxumLayer`] configured to use [`OpenTelemetry`'s conventional span field\n//! names][otel].\n//!\n//! # Span fields\n//!\n//! Try to provide some of the field define at\n//! [semantic-conventions/.../http-spans.md](https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/http/http-spans.md)\n//! (Please report or provide fix for missing one)\n//!\n//! # Example\n//!\n//! ```\n//! use axum::{Router, routing::get, http::Request};\n//! use axum_tracing_opentelemetry::middleware::OtelAxumLayer;\n//! use std::net::SocketAddr;\n//! use tower::ServiceBuilder;\n//!\n//! let app = Router::new()\n//!     .route(\"/\", get(|| async {}))\n//!     .layer(OtelAxumLayer::default());\n//!\n//! # async {\n//! let addr = &\"0.0.0.0:3000\".parse::<SocketAddr>().unwrap();\n//! let listener = tokio::net::TcpListener::bind(addr).await.unwrap();\n//! axum::serve(listener, app.into_make_service())\n//!     .await\n//!     .expect(\"server failed\");\n//! # };\n//! ```\n//!\n\nuse axum::extract::{ConnectInfo, MatchedPath};\nuse http::{Request, Response};\nuse opentelemetry_semantic_conventions::attribute::{CLIENT_ADDRESS, HTTP_ROUTE};\nuse pin_project_lite::pin_project;\nuse std::{\n    error::Error,\n    future::Future,\n    net::SocketAddr,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tower::{Layer, Service};\nuse tracing::Span;\nuse tracing_opentelemetry_instrumentation_sdk::http::{\n    self as otel_http, extract_client_ip_from_headers,\n};\n\n#[deprecated(\n    since = \"0.12.0\",\n    note = \"keep for transition, replaced by OtelAxumLayer\"\n)]\n#[must_use]\npub fn opentelemetry_tracing_layer() -> OtelAxumLayer {\n    OtelAxumLayer::default()\n}\n\npub type Filter = fn(&str) -> bool;\n\n/// layer/middleware for axum:\n///\n/// - propagate `OpenTelemetry` context (`trace_id`,...) to server\n/// - create a Span for `OpenTelemetry` (and tracing) on call\n///\n/// `OpenTelemetry` context are extracted from tracing's span.\n#[derive(Default, Debug, Clone)]\npub struct OtelAxumLayer {\n    filter: Option<Filter>,\n    try_extract_client_ip: bool,\n}\n\n// add a builder like api\nimpl OtelAxumLayer {\n    #[must_use]\n    pub fn filter(self, filter: Filter) -> Self {\n        let mut me = self;\n        me.filter = Some(filter);\n        me\n    }\n\n    /// Enable or disable (default) the extraction of client's ip.\n    /// Extraction from (in order):\n    ///\n    /// 1. http header 'Forwarded'\n    /// 2. http header `X-Forwarded-For`\n    /// 3. socket connection ip, use the `axum::extract::ConnectionInfo` (see [`Router::into_make_service_with_connect_info`] for more details)\n    /// 4. empty (failed to extract the information)\n    ///\n    /// The extracted value could an ip v4, ip v6, a string (as `Forwarded` can use label or hide the client).\n    /// The extracted value is stored it as `client.address` in the span/trace\n    ///\n    /// [`Router::into_make_service_with_connect_info`]: axum::routing::Router::into_make_service_with_connect_info\n    #[must_use]\n    pub fn try_extract_client_ip(self, enable: bool) -> Self {\n        let mut me = self;\n        me.try_extract_client_ip = enable;\n        me\n    }\n}\n\nimpl<S> Layer<S> for OtelAxumLayer {\n    /// The wrapped service\n    type Service = OtelAxumService<S>;\n    fn layer(&self, inner: S) -> Self::Service {\n        OtelAxumService {\n            inner,\n            filter: self.filter,\n            try_extract_client_ip: self.try_extract_client_ip,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct OtelAxumService<S> {\n    inner: S,\n    filter: Option<Filter>,\n    try_extract_client_ip: bool,\n}\n\nimpl<S, B, B2> Service<Request<B>> for OtelAxumService<S>\nwhere\n    S: Service<Request<B>, Response = Response<B2>> + Clone + Send + 'static,\n    S::Error: Error + 'static, //fmt::Display + 'static,\n    S::Future: Send + 'static,\n    B: Send + 'static,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    // #[allow(clippy::type_complexity)]\n    // type Future = futures_core::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;\n    type Future = ResponseFuture<S::Future>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx).map_err(Into::into)\n    }\n\n    fn call(&mut self, req: Request<B>) -> Self::Future {\n        use tracing_opentelemetry::OpenTelemetrySpanExt;\n        let req = req;\n        let span = if self.filter.is_none_or(|f| f(req.uri().path())) {\n            let route = http_route(&req);\n            let method = req.method();\n            let client_ip = if self.try_extract_client_ip {\n                extract_client_ip_from_headers(req.headers())\n                    .map(ToString::to_string)\n                    .or_else(|| {\n                        req.extensions()\n                            .get::<ConnectInfo<SocketAddr>>()\n                            .map(|ConnectInfo(client_ip)| client_ip.to_string())\n                    })\n            } else {\n                None\n            };\n\n            let span = otel_http::http_server::make_span_from_request(&req);\n            span.record(HTTP_ROUTE, route);\n            span.record(\"otel.name\", format!(\"{method} {route}\").trim());\n            if let Some(client_ip) = client_ip {\n                span.record(CLIENT_ADDRESS, client_ip);\n            }\n            if let Err(error) = span.set_parent(otel_http::extract_context(req.headers())) {\n                tracing::warn!(?error, \"can not set parent trace_id to span\");\n            }\n            span\n        } else {\n            tracing::Span::none()\n        };\n        let future = {\n            let _enter = span.enter();\n            self.inner.call(req)\n        };\n        ResponseFuture {\n            inner: future,\n            span,\n        }\n    }\n}\n\npin_project! {\n    /// Response future for [`Trace`].\n    ///\n    /// [`Trace`]: super::Trace\n    pub struct ResponseFuture<F> {\n        #[pin]\n        pub(crate) inner: F,\n        pub(crate) span: Span,\n        // pub(crate) start: Instant,\n    }\n}\n\nimpl<Fut, ResBody, E> Future for ResponseFuture<Fut>\nwhere\n    Fut: Future<Output = Result<Response<ResBody>, E>>,\n    E: std::error::Error + 'static,\n{\n    type Output = Result<Response<ResBody>, E>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let _guard = this.span.enter();\n        let result = futures_util::ready!(this.inner.poll(cx));\n        otel_http::http_server::update_span_from_response_or_error(this.span, &result);\n        Poll::Ready(result)\n    }\n}\n\n#[inline]\nfn http_route<B>(req: &Request<B>) -> &str {\n    req.extensions()\n        .get::<MatchedPath>()\n        .map_or_else(|| \"\", |mp| mp.as_str())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use axum::{Router, body::Body, routing::get};\n    use http::{Request, StatusCode};\n    use rstest::rstest;\n    use testing_tracing_opentelemetry::{FakeEnvironment, assert_trace};\n    use tower::Service;\n\n    #[rstest]\n    #[case(\"filled_http_route_for_existing_route\", \"http://example.com/users/123\", &[], false)]\n    #[case(\"empty_http_route_for_nonexisting_route\", \"/idontexist/123\", &[], false)]\n    #[case(\"status_code_on_close_for_ok\", \"/users/123\", &[], false)]\n    #[case(\"status_code_on_close_for_error\", \"/status/500\", &[], false)]\n    #[case(\"filled_http_headers\", \"/users/123\", &[(\"user-agent\", \"tests\"), (\"x-forwarded-for\", \"127.0.0.1\")], false)]\n    #[case(\"call_with_w3c_trace\", \"/users/123\", &[(\"traceparent\", \"00-b2611246a58fd7ea623d2264c5a1e226-b2c9b811f2f424af-01\")], true)]\n    #[case(\"trace_id_in_child_span\", \"/with_child_span\", &[], false)]\n    #[case(\"trace_id_in_child_span_for_remote\", \"/with_child_span\", &[(\"traceparent\", \"00-b2611246a58fd7ea623d2264c5a1e226-b2c9b811f2f424af-01\")], true)]\n    // failed to extract \"http.route\" before axum-0.6.15\n    // - https://github.com/davidB/axum-tracing-opentelemetry/pull/54 (reverted)\n    // - https://github.com/tokio-rs/axum/issues/1441#issuecomment-1272158039\n    #[case(\"extract_route_from_nested\", \"/nest/123\", &[], false)]\n    #[tokio::test(flavor = \"multi_thread\")]\n    async fn check_span_event(\n        #[case] name: &str,\n        #[case] uri: &str,\n        #[case] headers: &[(&str, &str)],\n        #[case] is_trace_id_constant: bool,\n    ) {\n        let mut fake_env = FakeEnvironment::setup().await;\n        {\n            let mut svc = Router::new()\n                .route(\"/users/{id}\", get(|| async { StatusCode::OK }))\n                .route(\n                    \"/status/500\",\n                    get(|| async { StatusCode::INTERNAL_SERVER_ERROR }),\n                )\n                .route(\n                    \"/with_child_span\",\n                    get(|| async {\n                        let span = tracing::span!(tracing::Level::INFO, \"my child span\");\n                        span.in_scope(|| {\n                            // Any trace events in this closure or code called by it will occur within\n                            // the span.\n                        });\n                        StatusCode::OK\n                    }),\n                )\n                .nest(\n                    \"/nest\",\n                    Router::new()\n                        .route(\"/{nest_id}\", get(|| async {}))\n                        .fallback(|| async { (StatusCode::NOT_FOUND, \"inner fallback\") }),\n                )\n                .fallback(|| async { (StatusCode::NOT_FOUND, \"outer fallback\") })\n                .layer(opentelemetry_tracing_layer());\n            let mut builder = Request::builder();\n            for (key, value) in headers {\n                builder = builder.header(*key, *value);\n            }\n            let req = builder.uri(uri).body(Body::empty()).unwrap();\n            let _res = svc.call(req).await.unwrap();\n\n            // while res.data().await.is_some() {}\n            // res.trailers().await.unwrap();\n            // drop(res);\n        }\n        let (tracing_events, otel_spans) = fake_env.collect_traces().await;\n        assert_trace(name, tracing_events, otel_spans, is_trace_id_constant);\n    }\n}\n"
  },
  {
    "path": "cliff.toml",
    "content": "# git-cliff ~ default configuration file\n# https://git-cliff.org/docs/configuration\n#\n# Lines starting with \"#\" are comments.\n# Configuration options are organized into tables and keys.\n# See documentation for more information on available options.\n# see https://github.com/orhun/git-cliff/blob/main/examples/keepachangelog.toml\n\n[changelog]\n# changelog header\nheader = \"\"\"\n<!-- markdownlint-disable MD024-->\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n\"\"\"\n# template for the changelog body\n# https://keats.github.io/tera/docs/#introduction\nbody = \"\"\"\n{% if version %}\\\n    ## [{{ version | trim_start_matches(pat=\"v\") }}] - {{ timestamp | date(format=\"%Y-%m-%d\") }}\n{% else %}\\\n    ## [Unreleased]\n{% endif %}\\\n{% for group, commits in commits | group_by(attribute=\"group\") %}\n    ### {{ group | upper_first }}\n    {% for commit in commits %}\n        - {{ commit.message | split(pat=\"\\\\n\") | first | upper_first }} ([{{ commit.id | truncate(length=7, end=\"\") }}]({{ commit.id }}))\\\n    {% endfor %}\n{% endfor %}\\n\n\"\"\"\n# remove the leading and trailing whitespace from the template\ntrim = true\n# changelog footer\nfooter = \"\"\"\n<!-- generated by git-cliff -->\n\"\"\"\n# postprocessors\npostprocessors = [\n  # { pattern = '<REPO>', replace = \"https://github.com/orhun/git-cliff\" }, # replace repository URL\n]\n[git]\n# parse the commits based on https://www.conventionalcommits.org\nconventional_commits = false\n# filter out the commits that are not conventional\nfilter_unconventional = false\n# process each line of a commit as an individual commit\nsplit_commits = false\n# regex for preprocessing the commit messages\ncommit_preprocessors = [\n  # { pattern = '\\((\\w+\\s)?#([0-9]+)\\)', replace = \"([#${2}](<REPO>/issues/${2}))\"}, # replace issue numbers\n]\n# regex for parsing and grouping commits\n# try to follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)\ncommit_parsers = [\n  { message = \"^(feat|✨|💥)\", group = \"Added\" },\n  { message = \"^(fix|🐛|🚑️|👽️)\", group = \"Fixed\" },\n  { message = \"^(doc|✏️|📝)\", group = \"Changed\" },\n  { message = \"^(perf|⚡️)\", group = \"Changed\" },\n  { message = \"^(refactor|🎨|🔥|♻️)\", group = \"Refactor\", skip = true },\n  { message = \"^(style|💄)\", group = \"Changed\" },\n  { message = \"^(test|✅)\", group = \"Fixed\", skip = true },\n  { message = \"^(chore\\\\(release\\\\): prepare for|🔖|🚀)\", skip = true },\n  { message = \"^(chore\\\\(deps\\\\)|⬇️|⬆️|➕|➖)\", group = \"Changed\" },\n  { message = \"^chore\\\\(pr\\\\)\", skip = true },\n  { message = \"^chore\\\\(pull\\\\)\", skip = true },\n  { message = \"^(chore|ci|💚|👷|🚧)\", group = \"Changed\", skip = true },\n  { message = \"^(🔒️|🔐)\", group = \"Security\" },\n  { body = \".*security\", group = \"Security\" },\n  { message = \"^revert\", group = \"Changed\" },\n  { message = \"^.*: add\", group = \"Added\" },\n  { message = \"^.*: support\", group = \"Added\" },\n  { message = \"^.*: remove\", group = \"Removed\" },\n  { message = \"^.*: delete\", group = \"Removed\" },\n]\n# protect breaking changes from being skipped due to matching a skipping commit_parser\nprotect_breaking_commits = false\n# filter out the commits that are not matched by commit parsers\nfilter_commits = true\n# regex for matching git tags\ntag_pattern = \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+.*\"\n\n# regex for skipping tags\nskip_tags = \"\"\n# regex for ignoring tags\nignore_tags = \"\"\n# sort the tags topologically\ntopo_order = false\n# sort the commits inside sections by oldest/newest order\nsort_commits = \"oldest\"\n# limit the number of commits included in the changelog.\n# limit_commits = 42\n"
  },
  {
    "path": "deny.toml",
    "content": "# This template contains all of the possible sections and their default values\n\n# Note that all fields that take a lint level have these possible values:\n# * deny - An error will be produced and the check will fail\n# * warn - A warning will be produced, but the check will not fail\n# * allow - No warning or error will be produced, though in some cases a note\n# will be\n\n# The values provided in this template are the default values that will be used\n# when any section or field is not specified in your own configuration\n\n# Root options\n\n# The graph table configures how the dependency graph is constructed and thus\n# which crates the checks are performed against\n[graph]\n# If 1 or more target triples (and optionally, target_features) are specified,\n# only the specified targets will be checked when running `cargo deny check`.\n# This means, if a particular package is only ever used as a target specific\n# dependency, such as, for example, the `nix` crate only being used via the\n# `target_family = \"unix\"` configuration, that only having windows targets in\n# this list would mean the nix crate, as well as any of its exclusive\n# dependencies not shared by any other crates, would be ignored, as the target\n# list here is effectively saying which targets you are building for.\ntargets = [\n  # The triple can be any string, but only the target triples built in to\n  # rustc (as of 1.40) can be checked against actual config expressions\n  # \"x86_64-unknown-linux-musl\",\n  # You can also specify which target_features you promise are enabled for a\n  # particular target. target_features are currently not validated against\n  # the actual valid features supported by the target architecture.\n  # { triple = \"wasm32-unknown-unknown\", features = [\"atomics\"] },\n]\n# When creating the dependency graph used as the source of truth when checks are\n# executed, this field can be used to prune crates from the graph, removing them\n# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate\n# is pruned from the graph, all of its dependencies will also be pruned unless\n# they are connected to another crate in the graph that hasn't been pruned,\n# so it should be used with care. The identifiers are [Package ID Specifications]\n# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)\n# exclude = []\n# If true, metadata will be collected with `--all-features`. Note that this can't\n# be toggled off if true, if you want to conditionally enable `--all-features` it\n# is recommended to pass `--all-features` on the cmd line instead\nall-features = false\n# If true, metadata will be collected with `--no-default-features`. The same\n# caveat with `all-features` applies\nno-default-features = false\n# If set, these feature will be enabled when collecting metadata. If `--features`\n# is specified on the cmd line they will take precedence over this option.\n# features = []\n\n# The output table provides options for how/if diagnostics are outputted\n[output]\n# When outputting inclusion graphs in diagnostics that include features, this\n# option can be used to specify the depth at which feature edges will be added.\n# This option is included since the graphs can be quite large and the addition\n# of features from the crate(s) to all of the graph roots can be far too verbose.\n# This option can be overridden via `--feature-depth` on the cmd line\nfeature-depth = 1\n\n# This section is considered when running `cargo deny check advisories`\n# More documentation for the advisories section can be found here:\n# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html\n[advisories]\n# The path where the advisory databases are cloned/fetched into\n# db-path = \"$CARGO_HOME/advisory-dbs\"\n# The url(s) of the advisory databases to use\n# db-urls = [\"https://github.com/rustsec/advisory-db\"]\n# A list of advisory IDs to ignore. Note that ignored advisories will still\n# output a note when they are encountered.\nignore = [\n  # \"RUSTSEC-0000-0000\",\n  # { id = \"RUSTSEC-0000-0000\", reason = \"you can specify a reason the advisory is ignored\" },\n  # \"a-crate-that-is-yanked@0.1.1\", # you can also ignore yanked crate versions if you wish\n  # { crate = \"a-crate-that-is-yanked@0.1.1\", reason = \"you can specify why you are ignoring the yanked crate\" },\n]\n# If this is true, then cargo deny will use the git executable to fetch advisory database.\n# If this is false, then it uses a built-in git library.\n# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.\n# See Git Authentication for more information about setting up git authentication.\n# git-fetch-with-cli = true\n\n# This section is considered when running `cargo deny check licenses`\n# More documentation for the licenses section can be found here:\n# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html\n[licenses]\n# List of explicitly allowed licenses\n# See https://spdx.org/licenses/ for list of possible licenses\n# [possible values: any SPDX 3.11 short identifier (+ optional exception)].\nallow = [\n  \"Apache-2.0\",\n  \"Apache-2.0 WITH LLVM-exception\",\n  \"BSD-2-Clause\",\n  \"BSD-3-Clause\",\n  \"CC0-1.0\",\n  \"ISC\",\n  \"MIT\",\n  # \"OpenSSL\",\n  \"Unicode-3.0\",\n  # \"Unicode-DFS-2016\",\n  \"Unlicense\",\n  \"Zlib\"\n]\n# The confidence threshold for detecting a license from license text.\n# The higher the value, the more closely the license text must be to the\n# canonical license text of a valid SPDX license file.\n# [possible values: any between 0.0 and 1.0].\nconfidence-threshold = 0.8\n# Allow 1 or more licenses on a per-crate basis, so that particular licenses\n# aren't accepted for every possible crate as with the normal allow list\nexceptions = [\n  # Each entry is the crate and version constraint, and its specific allow\n  # list\n  # { allow = [\"Zlib\"], crate = \"adler32\" },\n]\n\n# Some crates don't have (easily) machine readable licensing information,\n# adding a clarification entry for it allows you to manually specify the\n# licensing information\n[[licenses.clarify]]\n# The package spec the clarification applies to\ncrate = \"ring\"\n# The SPDX expression for the license requirements of the crate\nexpression = \"MIT AND ISC AND OpenSSL\"\nlicense-files = [\n  # Each entry is a crate relative path, and the (opaque) hash of its contents\n  { path = \"LICENSE\", hash = 0xbd0eed23 },\n]\n\n[licenses.private]\n# One or more files in the crate's source used as the \"source of truth\" for\n# the license expression. If the contents match, the clarification will be used\n# when running the license check, otherwise the clarification will be ignored\n# and the crate will be checked normally, which may produce warnings or errors\n# depending on the rest of your configuration\n# If true, ignores workspace crates that aren't published, or are only\n# published to private registries.\n# To see how to mark a crate as unpublished (to the official registry),\n# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.\nignore = false\n# One or more private registries that you might publish crates to, if a crate\n# is only published to private registries, and ignore is true, the crate will\n# not have its license(s) checked\nregistries = [\n  # \"https://sekretz.com/registry\n]\n\n# This section is considered when running `cargo deny check bans`.\n# More documentation about the 'bans' section can be found here:\n# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html\n[bans]\n# Lint level for when multiple versions of the same crate are detected\nmultiple-versions = \"deny\"\n# Lint level for when a crate version requirement is `*`\nwildcards = \"allow\"\n# The graph highlighting used when creating dotgraphs for crates\n# with multiple versions\n# * lowest-version - The path to the lowest versioned duplicate is highlighted\n# * simplest-path - The path to the version with the fewest edges is highlighted\n# * all - Both lowest-version and simplest-path are used\nhighlight = \"all\"\n# The default lint level for `default` features for crates that are members of\n# the workspace that is being checked. This can be overridden by allowing/denying\n# `default` on a crate-by-crate basis if desired.\nworkspace-default-features = \"allow\"\n# The default lint level for `default` features for external crates that are not\n# members of the workspace. This can be overridden by allowing/denying `default`\n# on a crate-by-crate basis if desired.\nexternal-default-features = \"allow\"\n# List of crates that are allowed. Use with care!\nallow = [\n  # \"ansi_term@0.11.0\",\n  # { crate = \"ansi_term@0.11.0\", reason = \"you can specify a reason it is allowed\" },\n]\n# List of crates to deny\ndeny = [\n  # \"ansi_term@0.11.0\",\n  # { crate = \"ansi_term@0.11.0\", reason = \"you can specify a reason it is banned\" },\n  # Wrapper crates can optionally be specified to allow the crate when it\n  # is a direct dependency of the otherwise banned crate\n  # { crate = \"ansi_term@0.11.0\", wrappers = [\"this-crate-directly-depends-on-ansi_term\"] },\n]\n\n# List of features to allow/deny\n# Each entry the name of a crate and a version range. If version is\n# not specified, all versions will be matched.\n# [[bans.features]]\n# crate = \"reqwest\"\n# Features to not allow\n# deny = [\"json\"]\n# Features to allow\n# allow = [\n#    \"rustls\",\n#    \"__rustls\",\n#    \"__tls\",\n#    \"hyper-rustls\",\n#    \"rustls\",\n#    \"rustls-pemfile\",\n#    \"rustls-tls-webpki-roots\",\n#    \"tokio-rustls\",\n#    \"webpki-roots\",\n# ]\n# If true, the allowed features must exactly match the enabled feature set. If\n# this is set there is no point setting `deny`\n# exact = true\n\n# Certain crates/versions that will be skipped when doing duplicate detection.\nskip = [\n  # \"ansi_term@0.11.0\",\n  # { crate = \"ansi_term@0.11.0\", reason = \"you can specify a reason why it can't be updated/removed\" },\n  # \"axum@0.7\",      # tonic depend on axum 0.7\n  # \"axum-core@0.4\", # tonic depend on axum 0.7\n  # \"matchit@0.7\",   # tonic depend on axum 0.7\n  # \"tower@0.4\",     # axum 0.7 use tower 0.5, but hyper still use 0.4\n  # \"sync_wrapper\",   # axum direct and transive dependency use multiple version\n  # \"regex-syntax\",\n  # \"regex-automata\",\n  # \"indexmap\",\n  # \"hermit-abi\",\n  # \"rustls-native-certs\",\n  \"hashbrown\",\n  \"getrandom\",\n  # \"rand\", # until tonic & tower upgrade\n  # \"rand_chacha\", # until tonic & tower upgrade\n  # \"rand_core\", # until tonic & tower upgrade\n  \"r-efi\",\n  # \"socket2\",\n  # \"wasi\",\n  \"wit-bindgen\",\n]\n# Similarly to `skip` allows you to skip certain crates during duplicate\n# detection. Unlike skip, it also includes the entire tree of transitive\n# dependencies starting at the specified crate, up to a certain depth, which is\n# by default infinite.\nskip-tree = [\n  # \"ansi_term@0.11.0\", # will be skipped along with _all_ of its direct and transitive dependencies\n  # { crate = \"ansi_term@0.11.0\", depth = 20 },\n  \"windows-targets\",\n  \"windows-sys\",\n  # \"async-std\",\n]\n\n# This section is considered when running `cargo deny check sources`.\n# More documentation about the 'sources' section can be found here:\n# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html\n[sources]\n# Lint level for what to happen when a crate from a crate registry that is not\n# in the allow list is encountered\nunknown-registry = \"warn\"\n# Lint level for what to happen when a crate from a git repository that is not\n# in the allow list is encountered\nunknown-git = \"warn\"\n# List of URLs for allowed crate registries. Defaults to the crates.io index\n# if not specified. If it is specified but empty, no registries are allowed.\nallow-registry = [\"https://github.com/rust-lang/crates.io-index\"]\n# List of URLs for allowed Git repositories\nallow-git = []\n\n[sources.allow-org]\n# github.com organizations to allow git sources for\ngithub = []\n# gitlab.com organizations to allow git sources for\ngitlab = []\n# bitbucket.org organizations to allow git sources for\nbitbucket = []\n"
  },
  {
    "path": "examples/axum-otlp/Cargo.toml",
    "content": "[package]\nname = \"examples-axum-otlp\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\naxum = { workspace = true, default-features = true }\naxum-tracing-opentelemetry = { path = \"../../axum-tracing-opentelemetry\" }\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n  \"otlp\",\n  \"tracing_subscriber_ext\",\n  \"metrics\"\n] }\nopentelemetry = { workspace = true }\nopentelemetry-otlp = { workspace = true, default-features = false, features = [\n  \"reqwest-rustls\",\n  \"http-proto\",\n  \"tls\",\n] }\nserde_json = \"1\"\ntokio = { workspace = true, features = [\"full\"] }\ntracing = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../../tracing-opentelemetry-instrumentation-sdk\" }\n"
  },
  {
    "path": "examples/axum-otlp/README.md",
    "content": "# `examples-axum-otlp`\n\nIn a terminal, run\n\nConfigure the [environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/) for the OTLP exporter:\n\n```sh\n# For GRPC:\nexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://localhost:4317\"\nexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=\"grpc\"\nexport OTEL_TRACES_SAMPLER=\"always_on\"\n\n# For HTTP:\nexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://127.0.0.1:4318/v1/traces\"\nexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=\"http/protobuf\"\nexport OTEL_TRACES_SAMPLER=\"always_on\"\n```\n\n```sh\n❯ cd examples/axum-otlp\n❯ cargo run\n   Compiling examples-axum-otlp v0.1.0 (/home/david/src/github.com/davidB/axum-tracing-opentelemetry/examples/axum-otlp)\n    Finished dev [unoptimized + debuginfo] target(s) in 3.60s\n     Running `/home/david/src/github.com/davidB/axum-tracing-opentelemetry/target/debug/examples-axum-otlp`\n     0.000041809s  INFO init_tracing_opentelemetry::tracing_subscriber_ext: init logging & tracing\n    at init-tracing-opentelemetry/src/tracing_subscriber_ext.rs:82 on main\n\n     0.000221695s DEBUG otel::setup::resource: key: service.name, value: unknown_service\n    at init-tracing-opentelemetry/src/resource.rs:63 on main\n\n     0.000242183s DEBUG otel::setup::resource: key: os.type, value: linux\n    at init-tracing-opentelemetry/src/resource.rs:63 on main\n\n     0.000280946s DEBUG otel::setup: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: \"http://localhost:4317\"\n    at init-tracing-opentelemetry/src/otlp.rs:22 on main\n\n     0.000293128s DEBUG otel::setup: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: \"grpc\"\n    at init-tracing-opentelemetry/src/otlp.rs:23 on main\n\n     0.000377897s DEBUG otel::setup: OTEL_TRACES_SAMPLER: \"always_on\"\n    at init-tracing-opentelemetry/src/otlp.rs:80 on main\n\n     0.000561931s DEBUG otel::setup: OTEL_PROPAGATORS: \"tracecontext,baggage\"\n    at init-tracing-opentelemetry/src/lib.rs:97 on main\n\n     0.000134291s  WARN examples_axum_otlp: listening on 0.0.0.0:3003\n    at examples/axum-otlp/src/main.rs:15 on main\n\n     0.000150401s  INFO examples_axum_otlp: try to call `curl -i http://127.0.0.1:3003/` (with trace)\n    at examples/axum-otlp/src/main.rs:16 on main\n\n     0.000159659s  INFO examples_axum_otlp: try to call `curl -i http://127.0.0.1:3003/health` (with NO trace)\n    at examples/axum-otlp/src/main.rs:17 on main\n...\n```\n\nInto an other terminal, call the `/` (endpoint with `OtelAxumLayer` and `OtelInResponseLayer`)\n\n```sh\n❯ curl -i http://127.0.0.1:3003/\nHTTP/1.1 200 OK\ncontent-type: application/json\ncontent-length: 50\ntraceparent: 00-b2611246a58fd7ea623d2264c5a1e226-b2c9b811f2f424af-01\ntracestate:\ndate: Wed, 28 Dec 2022 17:04:59 GMT\n\n{\"my_trace_id\":\"b2611246a58fd7ea623d2264c5a1e226\"}\n```\n\ncall the `/health` (endpoint with NO layer)\n\n```sh\n❯ curl -i http://127.0.0.1:3003/health\nHTTP/1.1 200 OK\ncontent-type: application/json\ncontent-length: 15\ndate: Wed, 28 Dec 2022 17:14:07 GMT\n\n{\"status\":\"UP\"}\n```\n"
  },
  {
    "path": "examples/axum-otlp/src/main.rs",
    "content": "#![allow(clippy::let_with_type_underscore)]\n#![allow(clippy::default_constructed_unit_structs)] // warning since 1.71\n\nuse axum::extract::Path;\nuse axum::{BoxError, Router, response::IntoResponse, routing::get};\nuse axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer};\nuse serde_json::json;\nuse std::net::SocketAddr;\nuse tracing_opentelemetry_instrumentation_sdk::find_current_trace_id;\n\n#[tokio::main]\nasync fn main() -> Result<(), BoxError> {\n    // very opinionated init of tracing, look as is source to make your own\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n\n    let app = app();\n    // run it\n    let addr = &\"0.0.0.0:3003\".parse::<SocketAddr>()?;\n    tracing::warn!(\"listening on {}\", addr);\n    tracing::info!(\"try to call `curl -i http://127.0.0.1:3003/` (with trace)\"); //Devskim: ignore DS137138\n    tracing::info!(\"try to call `curl -i http://127.0.0.1:3003/health` (with NO trace)\"); //Devskim: ignore DS137138\n    let listener = tokio::net::TcpListener::bind(addr).await?;\n    axum::serve(listener, app.into_make_service()).await?;\n    Ok(())\n}\n\nfn app() -> Router {\n    // build our application with a route\n    Router::new()\n        .route(\n            \"/proxy/{service}/{*path}\",\n            get(proxy_handler).post(proxy_handler),\n        )\n        .route(\"/\", get(index)) // request processed inside span\n        // include trace context as header into the response\n        .layer(OtelInResponseLayer::default())\n        //start OpenTelemetry trace on incoming request\n        .layer(OtelAxumLayer::default())\n        .route(\"/health\", get(health)) // request processed without span / trace\n}\n\nasync fn health() -> impl IntoResponse {\n    axum::Json(json!({ \"status\" : \"UP\" }))\n}\n\n#[tracing::instrument]\nasync fn index() -> impl IntoResponse {\n    tracing::info!(monotonic_counter.index = 1);\n    sleep_10ms().await;\n    sleep_10ms().await;\n    sleep_10ms().await;\n    let trace_id = find_current_trace_id();\n    dbg!(&trace_id);\n    //std::thread::sleep(std::time::Duration::from_secs(1));\n    axum::Json(json!({ \"my_trace_id\": trace_id }))\n}\n\n#[tracing::instrument]\nasync fn sleep_10ms() {\n    tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n}\n\nasync fn proxy_handler(Path((service, path)): Path<(String, String)>) -> impl IntoResponse {\n    // Overwrite the otel.name of the span\n    tracing::Span::current().record(\"otel.name\", format!(\"proxy {service}\"));\n    let trace_id = find_current_trace_id();\n    axum::Json(\n        json!({ \"my_trace_id\": trace_id, \"fake_proxy\": { \"service\": service, \"path\": path } }),\n    )\n}\n"
  },
  {
    "path": "examples/bug_234_tls/Cargo.toml",
    "content": "[package]\nname = \"bug_234_tls\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\naxum = { workspace = true, default-features = true }\naxum-tracing-opentelemetry = { path = \"../../axum-tracing-opentelemetry\" }\ntokio = { version = \"1\", features = [\"full\"] }\ntracing = { version = \"0.1\", features = [\"log\"] }\ntracing-subscriber = { version = \"0.3.19\", features = [\"env-filter\", \"std\"] }\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n    \"otlp\",\n    \"tracing_subscriber_ext\",\n    \"tls\",\n] }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../../tracing-opentelemetry-instrumentation-sdk\", features = [\n    \"http\",\n] }\n# opentelemetry = { version = \"0.29.1\", features = [\"metrics\"] }\n"
  },
  {
    "path": "examples/bug_234_tls/src/main.rs",
    "content": "use axum::{Router, response::Html, routing::get};\n\n#[tokio::main]\nasync fn main() {\n    // build our application with a route\n    let app = Router::new().route(\"/\", get(handler));\n\n    // run it\n    let listener = tokio::net::TcpListener::bind(\"127.0.0.1:3000\")\n        .await\n        .unwrap();\n    println!(\"listening on {}\", listener.local_addr().unwrap());\n    axum::serve(listener, app).await.unwrap();\n}\n\nasync fn handler() -> Html<&'static str> {\n    Html(\"<h1>Hello, World!</h1>\")\n}\n"
  },
  {
    "path": "examples/grpc/Cargo.toml",
    "content": "[package]\nname = \"examples-grpc\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n  \"otlp\",\n  \"tracing_subscriber_ext\",\n  \"logfmt\",\n] }\nopentelemetry = { workspace = true }\nprost = \"0.14\"\ntokio = { workspace = true, features = [\"full\"] }\ntonic = { workspace = true, features = [\"transport\", \"router\"] }\ntonic-health = \"0.14\"\ntonic-prost = \"0.14\"\ntonic-reflection = \"0.14\"\ntonic-tracing-opentelemetry = { path = \"../../tonic-tracing-opentelemetry\" }\ntower = { workspace = true }\ntracing = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../../tracing-opentelemetry-instrumentation-sdk\" }\n\n[build-dependencies]\ntonic-prost-build = \"0.14\"\n\n[[bin]]\nname = \"server\"\npath = \"src/server.rs\"\n\n[[bin]]\nname = \"client\"\npath = \"src/client.rs\"\n"
  },
  {
    "path": "examples/grpc/build.rs",
    "content": "use std::path::PathBuf;\n\nfn main() {\n    // trigger rebuild if \"proto\" folder change or empty\n    print!(\"cargo:rerun-if-changed=./proto\");\n    print!(\"cargo:rerun-if-changed=./src/generated\");\n\n    //let out_dir = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    let out_dir = PathBuf::from(std::env!(\"CARGO_MANIFEST_DIR\"))\n        .join(\"src\")\n        .join(\"generated\");\n    std::fs::create_dir_all(&out_dir).unwrap();\n\n    tonic_prost_build::configure()\n        .build_client(true)\n        .build_server(true)\n        .file_descriptor_set_path(out_dir.join(\"helloworld_descriptor.bin\"))\n        .out_dir(out_dir)\n        .compile_protos(&[\"helloworld.proto\"], &[\"proto\"])\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/grpc/proto/helloworld.proto",
    "content": "syntax = \"proto3\";\n\nimport \"google/protobuf/empty.proto\";\npackage helloworld;\n\nservice Greeter {\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n  rpc SayStatus (StatusRequest) returns (google.protobuf.Empty) {}\n}\n\nmessage HelloRequest {\n  string name = 1;\n}\n\nmessage HelloReply {\n  string message = 1;\n}\n\nmessage StatusRequest {\n  // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc\n  int32 code = 1;\n  string message = 2;\n}\n"
  },
  {
    "path": "examples/grpc/src/client.rs",
    "content": "use generated::greeter_client::GreeterClient;\nuse generated::{HelloRequest, StatusRequest};\nuse tonic::Code;\nuse tonic::transport::Channel;\nuse tonic_tracing_opentelemetry::middleware::client::OtelGrpcLayer;\nuse tower::ServiceBuilder;\n\npub mod generated {\n    //tonic::include_proto!(\"helloworld\");\n    include!(\"generated/helloworld.rs\");\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // very opinionated init of tracing, look as is source to make your own\n    let _guard = init_tracing_opentelemetry::TracingConfig::production()\n        .init_subscriber()\n        .expect(\"init subscribers\");\n\n    // let channel = Channel::from_static(\"http://[::1]:50051\").connect().await?;\n    let channel = Channel::from_static(\"http://127.0.0.1:50051\")\n        .connect()\n        .await?; //Devskim: ignore DS137138\n    let channel = ServiceBuilder::new().layer(OtelGrpcLayer).service(channel);\n\n    let mut client = GreeterClient::new(channel);\n    {\n        let request = tonic::Request::new(HelloRequest {\n            name: \"Tonic\".into(),\n        });\n\n        let response = client.say_hello(request).await?;\n\n        println!(\"RESPONSE={response:?}\");\n    }\n    {\n        let request = tonic::Request::new(StatusRequest {\n            code: Code::NotFound.into(),\n            message: \"not found...\".into(),\n        });\n\n        let response = client.say_status(request).await;\n\n        println!(\"RESPONSE={response:?}\");\n    }\n    {\n        let request = tonic::Request::new(StatusRequest {\n            code: Code::DeadlineExceeded.into(),\n            message: \"deadline...\".into(),\n        });\n\n        let response = client.say_status(request).await;\n\n        println!(\"RESPONSE={response:?}\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/grpc/src/generated/helloworld.rs",
    "content": "// This file is @generated by prost-build.\n#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]\npub struct HelloRequest {\n    #[prost(string, tag = \"1\")]\n    pub name: ::prost::alloc::string::String,\n}\n#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]\npub struct HelloReply {\n    #[prost(string, tag = \"1\")]\n    pub message: ::prost::alloc::string::String,\n}\n#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]\npub struct StatusRequest {\n    /// <https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc>\n    #[prost(int32, tag = \"1\")]\n    pub code: i32,\n    #[prost(string, tag = \"2\")]\n    pub message: ::prost::alloc::string::String,\n}\n/// Generated client implementations.\npub mod greeter_client {\n    #![allow(\n        unused_variables,\n        dead_code,\n        missing_docs,\n        clippy::wildcard_imports,\n        clippy::let_unit_value,\n    )]\n    use tonic::codegen::*;\n    use tonic::codegen::http::Uri;\n    #[derive(Debug, Clone)]\n    pub struct GreeterClient<T> {\n        inner: tonic::client::Grpc<T>,\n    }\n    impl GreeterClient<tonic::transport::Channel> {\n        /// Attempt to create a new client by connecting to a given endpoint.\n        pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>\n        where\n            D: TryInto<tonic::transport::Endpoint>,\n            D::Error: Into<StdError>,\n        {\n            let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;\n            Ok(Self::new(conn))\n        }\n    }\n    impl<T> GreeterClient<T>\n    where\n        T: tonic::client::GrpcService<tonic::body::Body>,\n        T::Error: Into<StdError>,\n        T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,\n        <T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,\n    {\n        pub fn new(inner: T) -> Self {\n            let inner = tonic::client::Grpc::new(inner);\n            Self { inner }\n        }\n        pub fn with_origin(inner: T, origin: Uri) -> Self {\n            let inner = tonic::client::Grpc::with_origin(inner, origin);\n            Self { inner }\n        }\n        pub fn with_interceptor<F>(\n            inner: T,\n            interceptor: F,\n        ) -> GreeterClient<InterceptedService<T, F>>\n        where\n            F: tonic::service::Interceptor,\n            T::ResponseBody: Default,\n            T: tonic::codegen::Service<\n                http::Request<tonic::body::Body>,\n                Response = http::Response<\n                    <T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody,\n                >,\n            >,\n            <T as tonic::codegen::Service<\n                http::Request<tonic::body::Body>,\n            >>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,\n        {\n            GreeterClient::new(InterceptedService::new(inner, interceptor))\n        }\n        /// Compress requests with the given encoding.\n        ///\n        /// This requires the server to support it otherwise it might respond with an\n        /// error.\n        #[must_use]\n        pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {\n            self.inner = self.inner.send_compressed(encoding);\n            self\n        }\n        /// Enable decompressing responses.\n        #[must_use]\n        pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {\n            self.inner = self.inner.accept_compressed(encoding);\n            self\n        }\n        /// Limits the maximum size of a decoded message.\n        ///\n        /// Default: `4MB`\n        #[must_use]\n        pub fn max_decoding_message_size(mut self, limit: usize) -> Self {\n            self.inner = self.inner.max_decoding_message_size(limit);\n            self\n        }\n        /// Limits the maximum size of an encoded message.\n        ///\n        /// Default: `usize::MAX`\n        #[must_use]\n        pub fn max_encoding_message_size(mut self, limit: usize) -> Self {\n            self.inner = self.inner.max_encoding_message_size(limit);\n            self\n        }\n        pub async fn say_hello(\n            &mut self,\n            request: impl tonic::IntoRequest<super::HelloRequest>,\n        ) -> std::result::Result<tonic::Response<super::HelloReply>, tonic::Status> {\n            self.inner\n                .ready()\n                .await\n                .map_err(|e| {\n                    tonic::Status::unknown(\n                        format!(\"Service was not ready: {}\", e.into()),\n                    )\n                })?;\n            let codec = tonic_prost::ProstCodec::default();\n            let path = http::uri::PathAndQuery::from_static(\n                \"/helloworld.Greeter/SayHello\",\n            );\n            let mut req = request.into_request();\n            req.extensions_mut()\n                .insert(GrpcMethod::new(\"helloworld.Greeter\", \"SayHello\"));\n            self.inner.unary(req, path, codec).await\n        }\n        pub async fn say_status(\n            &mut self,\n            request: impl tonic::IntoRequest<super::StatusRequest>,\n        ) -> std::result::Result<tonic::Response<()>, tonic::Status> {\n            self.inner\n                .ready()\n                .await\n                .map_err(|e| {\n                    tonic::Status::unknown(\n                        format!(\"Service was not ready: {}\", e.into()),\n                    )\n                })?;\n            let codec = tonic_prost::ProstCodec::default();\n            let path = http::uri::PathAndQuery::from_static(\n                \"/helloworld.Greeter/SayStatus\",\n            );\n            let mut req = request.into_request();\n            req.extensions_mut()\n                .insert(GrpcMethod::new(\"helloworld.Greeter\", \"SayStatus\"));\n            self.inner.unary(req, path, codec).await\n        }\n    }\n}\n/// Generated server implementations.\npub mod greeter_server {\n    #![allow(\n        unused_variables,\n        dead_code,\n        missing_docs,\n        clippy::wildcard_imports,\n        clippy::let_unit_value,\n    )]\n    use tonic::codegen::*;\n    /// Generated trait containing gRPC methods that should be implemented for use with GreeterServer.\n    #[async_trait]\n    pub trait Greeter: std::marker::Send + std::marker::Sync + 'static {\n        async fn say_hello(\n            &self,\n            request: tonic::Request<super::HelloRequest>,\n        ) -> std::result::Result<tonic::Response<super::HelloReply>, tonic::Status>;\n        async fn say_status(\n            &self,\n            request: tonic::Request<super::StatusRequest>,\n        ) -> std::result::Result<tonic::Response<()>, tonic::Status>;\n    }\n    #[derive(Debug)]\n    pub struct GreeterServer<T> {\n        inner: Arc<T>,\n        accept_compression_encodings: EnabledCompressionEncodings,\n        send_compression_encodings: EnabledCompressionEncodings,\n        max_decoding_message_size: Option<usize>,\n        max_encoding_message_size: Option<usize>,\n    }\n    impl<T> GreeterServer<T> {\n        pub fn new(inner: T) -> Self {\n            Self::from_arc(Arc::new(inner))\n        }\n        pub fn from_arc(inner: Arc<T>) -> Self {\n            Self {\n                inner,\n                accept_compression_encodings: Default::default(),\n                send_compression_encodings: Default::default(),\n                max_decoding_message_size: None,\n                max_encoding_message_size: None,\n            }\n        }\n        pub fn with_interceptor<F>(\n            inner: T,\n            interceptor: F,\n        ) -> InterceptedService<Self, F>\n        where\n            F: tonic::service::Interceptor,\n        {\n            InterceptedService::new(Self::new(inner), interceptor)\n        }\n        /// Enable decompressing requests with the given encoding.\n        #[must_use]\n        pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {\n            self.accept_compression_encodings.enable(encoding);\n            self\n        }\n        /// Compress responses with the given encoding, if the client supports it.\n        #[must_use]\n        pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {\n            self.send_compression_encodings.enable(encoding);\n            self\n        }\n        /// Limits the maximum size of a decoded message.\n        ///\n        /// Default: `4MB`\n        #[must_use]\n        pub fn max_decoding_message_size(mut self, limit: usize) -> Self {\n            self.max_decoding_message_size = Some(limit);\n            self\n        }\n        /// Limits the maximum size of an encoded message.\n        ///\n        /// Default: `usize::MAX`\n        #[must_use]\n        pub fn max_encoding_message_size(mut self, limit: usize) -> Self {\n            self.max_encoding_message_size = Some(limit);\n            self\n        }\n    }\n    impl<T, B> tonic::codegen::Service<http::Request<B>> for GreeterServer<T>\n    where\n        T: Greeter,\n        B: Body + std::marker::Send + 'static,\n        B::Error: Into<StdError> + std::marker::Send + 'static,\n    {\n        type Response = http::Response<tonic::body::Body>;\n        type Error = std::convert::Infallible;\n        type Future = BoxFuture<Self::Response, Self::Error>;\n        fn poll_ready(\n            &mut self,\n            _cx: &mut Context<'_>,\n        ) -> Poll<std::result::Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n        fn call(&mut self, req: http::Request<B>) -> Self::Future {\n            match req.uri().path() {\n                \"/helloworld.Greeter/SayHello\" => {\n                    #[allow(non_camel_case_types)]\n                    struct SayHelloSvc<T: Greeter>(pub Arc<T>);\n                    impl<T: Greeter> tonic::server::UnaryService<super::HelloRequest>\n                    for SayHelloSvc<T> {\n                        type Response = super::HelloReply;\n                        type Future = BoxFuture<\n                            tonic::Response<Self::Response>,\n                            tonic::Status,\n                        >;\n                        fn call(\n                            &mut self,\n                            request: tonic::Request<super::HelloRequest>,\n                        ) -> Self::Future {\n                            let inner = Arc::clone(&self.0);\n                            let fut = async move {\n                                <T as Greeter>::say_hello(&inner, request).await\n                            };\n                            Box::pin(fut)\n                        }\n                    }\n                    let accept_compression_encodings = self.accept_compression_encodings;\n                    let send_compression_encodings = self.send_compression_encodings;\n                    let max_decoding_message_size = self.max_decoding_message_size;\n                    let max_encoding_message_size = self.max_encoding_message_size;\n                    let inner = self.inner.clone();\n                    let fut = async move {\n                        let method = SayHelloSvc(inner);\n                        let codec = tonic_prost::ProstCodec::default();\n                        let mut grpc = tonic::server::Grpc::new(codec)\n                            .apply_compression_config(\n                                accept_compression_encodings,\n                                send_compression_encodings,\n                            )\n                            .apply_max_message_size_config(\n                                max_decoding_message_size,\n                                max_encoding_message_size,\n                            );\n                        let res = grpc.unary(method, req).await;\n                        Ok(res)\n                    };\n                    Box::pin(fut)\n                }\n                \"/helloworld.Greeter/SayStatus\" => {\n                    #[allow(non_camel_case_types)]\n                    struct SayStatusSvc<T: Greeter>(pub Arc<T>);\n                    impl<T: Greeter> tonic::server::UnaryService<super::StatusRequest>\n                    for SayStatusSvc<T> {\n                        type Response = ();\n                        type Future = BoxFuture<\n                            tonic::Response<Self::Response>,\n                            tonic::Status,\n                        >;\n                        fn call(\n                            &mut self,\n                            request: tonic::Request<super::StatusRequest>,\n                        ) -> Self::Future {\n                            let inner = Arc::clone(&self.0);\n                            let fut = async move {\n                                <T as Greeter>::say_status(&inner, request).await\n                            };\n                            Box::pin(fut)\n                        }\n                    }\n                    let accept_compression_encodings = self.accept_compression_encodings;\n                    let send_compression_encodings = self.send_compression_encodings;\n                    let max_decoding_message_size = self.max_decoding_message_size;\n                    let max_encoding_message_size = self.max_encoding_message_size;\n                    let inner = self.inner.clone();\n                    let fut = async move {\n                        let method = SayStatusSvc(inner);\n                        let codec = tonic_prost::ProstCodec::default();\n                        let mut grpc = tonic::server::Grpc::new(codec)\n                            .apply_compression_config(\n                                accept_compression_encodings,\n                                send_compression_encodings,\n                            )\n                            .apply_max_message_size_config(\n                                max_decoding_message_size,\n                                max_encoding_message_size,\n                            );\n                        let res = grpc.unary(method, req).await;\n                        Ok(res)\n                    };\n                    Box::pin(fut)\n                }\n                _ => {\n                    Box::pin(async move {\n                        let mut response = http::Response::new(\n                            tonic::body::Body::default(),\n                        );\n                        let headers = response.headers_mut();\n                        headers\n                            .insert(\n                                tonic::Status::GRPC_STATUS,\n                                (tonic::Code::Unimplemented as i32).into(),\n                            );\n                        headers\n                            .insert(\n                                http::header::CONTENT_TYPE,\n                                tonic::metadata::GRPC_CONTENT_TYPE,\n                            );\n                        Ok(response)\n                    })\n                }\n            }\n        }\n    }\n    impl<T> Clone for GreeterServer<T> {\n        fn clone(&self) -> Self {\n            let inner = self.inner.clone();\n            Self {\n                inner,\n                accept_compression_encodings: self.accept_compression_encodings,\n                send_compression_encodings: self.send_compression_encodings,\n                max_decoding_message_size: self.max_decoding_message_size,\n                max_encoding_message_size: self.max_encoding_message_size,\n            }\n        }\n    }\n    /// Generated gRPC service name\n    pub const SERVICE_NAME: &str = \"helloworld.Greeter\";\n    impl<T> tonic::server::NamedService for GreeterServer<T> {\n        const NAME: &'static str = SERVICE_NAME;\n    }\n}\n"
  },
  {
    "path": "examples/grpc/src/server.rs",
    "content": "use generated::greeter_server::{Greeter, GreeterServer};\nuse generated::{HelloReply, HelloRequest, StatusRequest};\nuse tonic::Code;\nuse tonic::{Request, Response, Status, transport::Server};\nuse tonic_tracing_opentelemetry::middleware::{filters, server};\n\npub mod generated {\n    //tonic::include_proto!(\"helloworld\");\n    include!(\"generated/helloworld.rs\");\n\n    pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =\n        //tonic::include_file_descriptor_set!(\"helloworld_descriptor\");\n        include_bytes!(\"generated/helloworld_descriptor.bin\");\n}\n\n#[derive(Default)]\npub struct MyGreeter {}\n\n#[tonic::async_trait]\nimpl Greeter for MyGreeter {\n    #[tracing::instrument(skip(self, request))]\n    async fn say_hello(\n        &self,\n        request: Request<HelloRequest>,\n    ) -> Result<Response<HelloReply>, Status> {\n        let trace_id = tracing_opentelemetry_instrumentation_sdk::find_current_trace_id();\n        tracing::info!(\n            \"Got a request from {:?} ({:?})\",\n            request.remote_addr(),\n            trace_id\n        );\n\n        let reply = generated::HelloReply {\n            message: format!(\"Hello {}! ({:?})\", request.into_inner().name, trace_id),\n        };\n        Ok(Response::new(reply))\n    }\n\n    #[tracing::instrument(skip(self, request))]\n    async fn say_status(&self, request: Request<StatusRequest>) -> Result<Response<()>, Status> {\n        let trace_id = tracing_opentelemetry_instrumentation_sdk::find_current_trace_id();\n        let request = request.into_inner();\n        tracing::info!(\"ask to return status : {} ({:?})\", request.code, trace_id);\n        Err(Status::new(Code::from(request.code), request.message))\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // very opinionated init of tracing, look as is source to make your own\n    let _guard = init_tracing_opentelemetry::TracingConfig::production()\n        .init_subscriber()\n        .expect(\"init subscribers\");\n\n    let addr = \"0.0.0.0:50051\".parse()?;\n    let greeter = MyGreeter::default();\n\n    let (_, health_service) = tonic_health::server::health_reporter();\n    let reflection_service = tonic_reflection::server::Builder::configure()\n        .register_encoded_file_descriptor_set(generated::FILE_DESCRIPTOR_SET)\n        .build_v1()?;\n\n    println!(\"GreeterServer listening on {addr}\");\n\n    Server::builder()\n        .timeout(std::time::Duration::from_secs(10))\n        // create trace for every request including health_service\n        .layer(server::OtelGrpcLayer::default().filter(filters::reject_healthcheck))\n        .add_service(health_service)\n        .add_service(reflection_service)\n        //.add_service(GreeterServer::new(greeter))\n        .add_service(GreeterServer::new(greeter))\n        .serve(addr)\n        .await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/init-tracing-with/.cargo/config.toml",
    "content": "[build]\nrustflags = [\"--cfg\", \"tokio_unstable\"]\n"
  },
  {
    "path": "examples/init-tracing-with/Cargo.toml",
    "content": "[package]\nname = \"init-tracing-with\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n  \"tracing_subscriber_ext\",\n] }\ntokio = { version = \"1.48.0\", features = [\n  \"macros\",\n  \"rt-multi-thread\",\n  \"tracing\",\n] }\ntokio-blocked = \"0.1.0\"\ntracing = { workspace = true }\ntracing-subscriber = \"0.3\"\n"
  },
  {
    "path": "examples/init-tracing-with/src/main.rs",
    "content": "use init_tracing_opentelemetry::TracingConfig;\nuse tokio_blocked::TokioBlockedLayer;\nuse tracing::info;\nuse tracing_subscriber::layer::SubscriberExt;\n\n#[tokio::main]\nasync fn main() {\n    let blocked = TokioBlockedLayer::new()\n        .with_warn_busy_single_poll(Some(std::time::Duration::from_micros(150)));\n\n    let _guard = TracingConfig::default()\n        .with_log_directives(\"info,tokio::task=trace,tokio::task::waker=warn\")\n        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::NONE)\n        .init_subscriber_ext(|subscriber| subscriber.with(blocked))\n        .unwrap();\n\n    info!(\"will block in 1 secs\");\n    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;\n\n    tokio::task::spawn(async {\n        // BAD!\n        // This produces a warning log message.\n        info!(\"blocking!\");\n        std::thread::sleep(std::time::Duration::from_secs(1));\n    })\n    .await\n    .unwrap();\n\n    // sleep().await;\n\n    tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;\n}\n\n// #[tracing::instrument]\n// async fn sleep() {\n//     tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;\n// }\n"
  },
  {
    "path": "examples/load/Cargo.toml",
    "content": "[package]\nname = \"examples-load\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n  \"otlp\",\n  \"tracing_subscriber_ext\",\n] }\nmemory-stats = \"1\"\nopentelemetry = { workspace = true }\nserde_json = \"1\"\ntokio = { version = \"1\", features = [\"full\"] }\ntracing = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../../tracing-opentelemetry-instrumentation-sdk\" }\n"
  },
  {
    "path": "examples/load/README.md",
    "content": "# examples use to\n\nNot an example, but a \"load application\" used to measure memory usage (+/-)\n\n```sh\n> bash -c \"cargo run --release 2>/dev/null\"\n...\n13s Current memory usage: Some(MemoryStats { physical_mem: 22814720, virtual_mem: 1123610624 })\n13s Current memory usage: Some(MemoryStats { physical_mem: 22835200, virtual_mem: 1123610624 })\n13s Current memory usage: Some(MemoryStats { physical_mem: 22859776, virtual_mem: 1123610624 })\n13s Current memory usage: Some(MemoryStats { physical_mem: 22863872, virtual_mem: 1123610624 })\n14s Current memory usage: Some(MemoryStats { physical_mem: 22867968, virtual_mem: 1123610624 })\n14s Current memory usage: Some(MemoryStats { physical_mem: 22876160, virtual_mem: 1123610624 })\n14s Current memory usage: Some(MemoryStats { physical_mem: 22888448, virtual_mem: 1123610624 })\n15s Current memory usage: Some(MemoryStats { physical_mem: 22892544, virtual_mem: 1123610624 })\n15s Current memory usage: Some(MemoryStats { physical_mem: 22896640, virtual_mem: 1123610624 })\n15s Current memory usage: Some(MemoryStats { physical_mem: 22904832, virtual_mem: 1123610624 })\n16s Current memory usage: Some(MemoryStats { physical_mem: 22921216, virtual_mem: 1123610624 })\n16s Current memory usage: Some(MemoryStats { physical_mem: 22933504, virtual_mem: 1123610624 })\n16s Current memory usage: Some(MemoryStats { physical_mem: 22937600, virtual_mem: 1123610624 })\n16s Current memory usage: Some(MemoryStats { physical_mem: 22941696, virtual_mem: 1123610624 })\n22s Current memory usage: Some(MemoryStats { physical_mem: 22945792, virtual_mem: 1123610624 })\n22s Current memory usage: Some(MemoryStats { physical_mem: 22949888, virtual_mem: 1123610624 })\n28s Current memory usage: Some(MemoryStats { physical_mem: 22970368, virtual_mem: 1123610624 })\n36s Current memory usage: Some(MemoryStats { physical_mem: 22999040, virtual_mem: 1123815424 })\n36s Current memory usage: Some(MemoryStats { physical_mem: 23003136, virtual_mem: 1123815424 })\n36s Current memory usage: Some(MemoryStats { physical_mem: 23007232, virtual_mem: 1123815424 })\n36s Current memory usage: Some(MemoryStats { physical_mem: 23011328, virtual_mem: 1123815424 })\n37s Current memory usage: Some(MemoryStats { physical_mem: 23015424, virtual_mem: 1123815424 })\n38s Current memory usage: Some(MemoryStats { physical_mem: 23207936, virtual_mem: 1123815424 })\n38s Current memory usage: Some(MemoryStats { physical_mem: 22712320, virtual_mem: 1123299328 })\n38s Current memory usage: Some(MemoryStats { physical_mem: 22786048, virtual_mem: 1123459072 })\n38s Current memory usage: Some(MemoryStats { physical_mem: 22872064, virtual_mem: 1123688448 })\n38s Current memory usage: Some(MemoryStats { physical_mem: 22876160, virtual_mem: 1123688448 })\n39s Current memory usage: Some(MemoryStats { physical_mem: 22880256, virtual_mem: 1123688448 })\n40s Current memory usage: Some(MemoryStats { physical_mem: 22888448, virtual_mem: 1123688448 })\n40s Current memory usage: Some(MemoryStats { physical_mem: 22904832, virtual_mem: 1123688448 })\n40s Current memory usage: Some(MemoryStats { physical_mem: 22921216, virtual_mem: 1123688448 })\n...\n\n```\n"
  },
  {
    "path": "examples/load/src/main.rs",
    "content": "use std::time::Instant;\n\nuse memory_stats::memory_stats;\nuse tracing::field::Empty;\nuse tracing_opentelemetry_instrumentation_sdk::otel_trace_span;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // very opinionated init of tracing, look as is source to make your own\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n    let mut stats = memory_stats();\n    if stats.is_none() {\n        eprintln!(\"Couldn't get the current memory usage :(\");\n        return Ok(());\n    }\n    let start = Instant::now();\n    loop {\n        let prev_stats = stats;\n        stats = memory_stats();\n        if stats != prev_stats {\n            println!(\n                \"{}s Current memory usage: {:?}\",\n                start.elapsed().as_secs(),\n                stats\n            );\n        }\n        for _i in 1..10000 {\n            let _span = otel_trace_span!(\n                \"Load\",\n                http.request.method = \"GET\",\n                http.route = Empty,\n                network.protocol.version = \"1.1\",\n                http.client.address = Empty,\n                http.response.status_code = Empty,\n                otel.kind = \"Sever\",\n                otel.status_code = Empty,\n                trace_id = Empty,\n                request_id = Empty,\n                exception.message = Empty,\n                //\"span.type\" = SpanType::Web.to_string(),\n            )\n            .entered();\n            //eprintln!(\"trace_id: {:?}\", tracing_opentelemetry_instrumentation_sdk::find_current_trace_id());\n        }\n    }\n}\n"
  },
  {
    "path": "examples/logging/Cargo.toml",
    "content": "[package]\nname = \"examples-logging\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\ninit-tracing-opentelemetry = { path = \"../../init-tracing-opentelemetry\", features = [\n  \"otlp\",\n  \"tracing_subscriber_ext\",\n  \"logs\"\n] }\nmemory-stats = \"1\"\nopentelemetry = { workspace = true }\nserde_json = \"1\"\ntokio = { version = \"1\", features = [\"full\"] }\ntracing = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../../tracing-opentelemetry-instrumentation-sdk\" }\n"
  },
  {
    "path": "examples/logging/src/main.rs",
    "content": "#[tracing::instrument]\nasync fn log() {\n    tracing::error!(\"This is ground control to Major Tom\");\n    tracing::warn!(\"Houston, we have a problem\");\n    tracing::info!(\"We have contact\");\n    tracing::debug!(\"Roger, copy that\");\n    tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;\n}\n\n#[tracing::instrument]\nasync fn calc(a: i32, b: i32) {\n    let result = a + b;\n    tracing::info!(result, \"calculated result\");\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // setting up tracing\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n\n    log().await;\n    calc(1, 2).await;\n\n    Ok(())\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.34.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.34.0...fake-opentelemetry-collector-v0.34.1) - 2026-03-15\n\n### <!-- 1 -->Fixed\n\n- MSRV (bump to 1.88) & api changes in dependencies, reformat ([#325](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/325))\n\n## [0.34.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.33.1...fake-opentelemetry-collector-v0.34.0) - 2026-01-19\n\n### <!-- 2 -->Added\n\n- *(deps)* update to rust 1.87 & edition 2024 ([#317](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/317))\n\n## [0.33.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.32.0...fake-opentelemetry-collector-v0.33.0) - 2025-11-22\n\n### <!-- 2 -->Added\n\n- *(metrics)* add support for metrics to the `fake-opentelemetry-collector` ([#302](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/302))\n\n## [0.32.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.28.0...fake-opentelemetry-collector-v0.32.0) - 2025-06-03\n\n### <!-- 2 -->Added\n\n- *(deps)* update opentelemetry 0.30 & tonic 0.13 (#240)\n\n## [0.28.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.26.1...fake-opentelemetry-collector-v0.28.0) - 2025-03-31\n\n### <!-- 2 -->Added\n\n- *(deps)* update opentelemetry to 0.29 (#227)\n\n## [0.26.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.26.0...fake-opentelemetry-collector-v0.26.1) - 2025-02-26\n\n### <!-- 3 -->Removed\n\n- *(deps)* remove minor constraint when major > 1\n\n## [0.25.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.24.0...fake-opentelemetry-collector-v0.25.0) - 2024-11-24\n\n### <!-- 1 -->Fixed\n\n- [**breaking**] use `TraceProvider::flush_force()` during test\n\n## [0.21.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.20.0...fake-opentelemetry-collector-v0.21.0) - 2024-09-22\n\n### <!-- 2 -->Added\n\n- *(deps)* upgrade to opentelemetry 0.25\n\n## [0.17.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/fake-opentelemetry-collector-v0.17.0...fake-opentelemetry-collector-v0.17.1) - 2024-02-24\n\n### Other\n- 👷 tune release-plz\n"
  },
  {
    "path": "fake-opentelemetry-collector/Cargo.toml",
    "content": "[package]\nname = \"fake-opentelemetry-collector\"\ndescription = \"A Fake (basic) opentelemetry collector, useful to test what is collected opentelemetry\"\nreadme = \"README.md\"\nkeywords = [\"tracing\", \"opentelemetry\", \"faker\", \"mock\"]\ncategories = [\"development-tools::testing\"]\nedition.workspace = true\nversion = \"0.34.1\"\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\nfutures = \"0.3\"\nhex = \"0.4\"\nopentelemetry = { workspace = true }\nopentelemetry-otlp = { workspace = true, features = [\n  \"grpc-tonic\",\n  \"logs\",\n  \"trace\",\n  \"metrics\",\n] }\nopentelemetry-proto = { workspace = true, features = [\n  \"gen-tonic\",\n  \"logs\",\n  \"trace\",\n] }\n# need tokio runtime to run smoke tests.\nopentelemetry_sdk = { workspace = true, features = [\n  \"trace\",\n  \"rt-tokio\",\n  \"testing\",\n] }\nserde = { version = \"1\", features = [\"derive\"] }\ntokio = { workspace = true, features = [\"full\"] }\ntokio-stream = { workspace = true, features = [\"net\"] }\ntonic = { workspace = true, features = [\"codegen\", \"transport\", \"router\"]}\ntracing = { workspace = true }\n\n[dev-dependencies]\nassert2 = { workspace = true }\ninsta = { workspace = true }\n"
  },
  {
    "path": "fake-opentelemetry-collector/README.md",
    "content": "# fake-opentelemetry-collector\n\nA Fake (basic) opentelemetry collector, useful to test what is collected by opentelemetry\n\nUsage example with [insta](https://crates.io/crates/insta) (snapshot testing)\n\n```rust\nuse std::time::Duration;\n\nuse fake_opentelemetry_collector::{setup_tracer_provider, FakeCollectorServer};\nuse opentelemetry::trace::TracerProvider;\nuse opentelemetry::trace::{Span, SpanKind, Tracer};\nuse tracing::debug;\n\n#[tokio::test(flavor = \"multi_thread\")]\nasync fn demo_fake_tracer_and_collector() {\n    debug!(\"Start the fake collector\");\n    let mut fake_collector = FakeCollectorServer::start()\n        .await\n        .expect(\"fake collector setup and started\");\n\n    debug!(\"Init the 'application' & tracer provider\");\n    let tracer_provider = setup_tracer_provider(&fake_collector).await;\n    let tracer = tracer_provider.tracer(\"test\");\n\n    debug!(\"Run the 'application' & sending span...\");\n    let mut span = tracer\n        .span_builder(\"my-test-span\")\n        .with_kind(SpanKind::Server)\n        .start(&tracer);\n    span.add_event(\"my-test-event\", vec![]);\n    span.end();\n\n    debug!(\"Shutdown the 'application' & tracer provider and force flush the spans\");\n    let _ = tracer_provider.force_flush();\n    tracer_provider\n        .shutdown()\n        .expect(\"no error during shutdown\");\n    drop(tracer_provider);\n\n    debug!(\"Collect & check the spans\");\n    let otel_spans = fake_collector\n        .exported_spans(1, Duration::from_secs(20))\n        .await;\n    //insta::assert_debug_snapshot!(otel_spans);\n    insta::assert_yaml_snapshot!(otel_spans, {\n        \"[].start_time_unix_nano\" => \"[timestamp]\",\n        \"[].end_time_unix_nano\" => \"[timestamp]\",\n        \"[].events[].time_unix_nano\" => \"[timestamp]\",\n        \"[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", trace_id.len())\n        }),\n        \"[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].links[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", trace_id.len())\n        }),\n        \"[].links[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n    });\n}\n```\n\ntest example at <https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/fake-opentelemetry-collector/tests>\n"
  },
  {
    "path": "fake-opentelemetry-collector/src/common.rs",
    "content": "use std::collections::BTreeMap;\n\npub(crate) fn cnv_attributes(\n    attributes: &[opentelemetry_proto::tonic::common::v1::KeyValue],\n) -> BTreeMap<String, String> {\n    attributes\n        .iter()\n        .map(|kv| (kv.key.to_string(), format!(\"{:?}\", kv.value)))\n        .collect::<BTreeMap<String, String>>()\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/src/lib.rs",
    "content": "mod common;\nmod logs;\nmod metrics;\nmod trace;\n\nuse logs::*;\nuse metrics::*;\nuse trace::*;\n\npub use logs::ExportedLog;\npub use metrics::ExportedMetric;\npub use trace::ExportedSpan;\n\nuse futures::StreamExt;\nuse opentelemetry_otlp::{LogExporter, MetricExporter, SpanExporter, WithExportConfig};\nuse opentelemetry_proto::tonic::collector::logs::v1::logs_service_server::LogsServiceServer;\nuse opentelemetry_proto::tonic::collector::metrics::v1::metrics_service_server::MetricsServiceServer;\nuse opentelemetry_proto::tonic::collector::trace::v1::trace_service_server::TraceServiceServer;\nuse opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};\nuse std::net::SocketAddr;\nuse std::time::{Duration, Instant};\nuse tokio::sync::mpsc;\nuse tokio::sync::mpsc::Receiver;\nuse tokio_stream::wrappers::TcpListenerStream;\nuse tracing::debug;\n\npub struct FakeCollectorServer {\n    address: SocketAddr,\n    req_rx: mpsc::Receiver<ExportedSpan>,\n    log_rx: mpsc::Receiver<ExportedLog>,\n    metrics_rx: mpsc::Receiver<ExportedMetric>,\n    handle: tokio::task::JoinHandle<()>,\n}\n\nimpl FakeCollectorServer {\n    pub async fn start() -> Result<Self, Box<dyn std::error::Error>> {\n        let addr: SocketAddr = \"127.0.0.1:0\".parse().unwrap();\n        let listener = tokio::net::TcpListener::bind(addr).await?;\n        let addr = listener.local_addr()?;\n        let stream = TcpListenerStream::new(listener).map(|s| {\n            if let Ok(ref s) = s {\n                debug!(\"Got new conn at {}\", s.peer_addr()?);\n            }\n            s\n        });\n\n        let (req_tx, req_rx) = mpsc::channel::<ExportedSpan>(64);\n        let (log_tx, log_rx) = mpsc::channel::<ExportedLog>(64);\n        let (metrics_tx, metrics_rx) = mpsc::channel::<ExportedMetric>(64);\n        let trace_service = TraceServiceServer::new(FakeTraceService::new(req_tx));\n        let logs_service = LogsServiceServer::new(FakeLogsService::new(log_tx));\n        let metrics_service = MetricsServiceServer::new(FakeMetricsService::new(metrics_tx));\n        let handle = tokio::task::spawn(async move {\n            debug!(\"start FakeCollectorServer http://{addr}\"); //Devskim: ignore DS137138)\n            tonic::transport::Server::builder()\n                .add_service(trace_service)\n                .add_service(logs_service)\n                .add_service(metrics_service)\n                .serve_with_incoming(stream)\n                .await\n                .expect(\"Server failed\");\n            debug!(\"stop FakeCollectorServer\");\n        });\n        Ok(Self {\n            address: addr,\n            req_rx,\n            log_rx,\n            metrics_rx,\n            handle,\n        })\n    }\n\n    pub fn address(&self) -> SocketAddr {\n        self.address\n    }\n\n    pub fn endpoint(&self) -> String {\n        format!(\"http://{}\", self.address()) //Devskim: ignore DS137138)\n    }\n\n    pub async fn exported_spans(\n        &mut self,\n        at_least: usize,\n        timeout: Duration,\n    ) -> Vec<ExportedSpan> {\n        recv_many(&mut self.req_rx, at_least, timeout).await\n    }\n\n    pub async fn exported_logs(&mut self, at_least: usize, timeout: Duration) -> Vec<ExportedLog> {\n        recv_many(&mut self.log_rx, at_least, timeout).await\n    }\n\n    pub async fn exported_metrics(\n        &mut self,\n        at_least: usize,\n        timeout: Duration,\n    ) -> Vec<ExportedMetric> {\n        recv_many(&mut self.metrics_rx, at_least, timeout).await\n    }\n\n    pub fn abort(self) {\n        self.handle.abort()\n    }\n}\n\nasync fn recv_many<T>(rx: &mut Receiver<T>, at_least: usize, timeout: Duration) -> Vec<T> {\n    let deadline = Instant::now();\n    let pause = (timeout / 10).min(Duration::from_millis(10));\n    while rx.len() < at_least && deadline.elapsed() < timeout {\n        tokio::time::sleep(pause).await;\n    }\n    std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>()\n}\n\npub async fn setup_tracer_provider(\n    fake_server: &FakeCollectorServer,\n) -> opentelemetry_sdk::trace::SdkTracerProvider {\n    // if the environment variable is set (in test or in caller), `with_endpoint` value is ignored\n    // if std::env::var(\"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT\").is_ok() {\n    //     panic!(\n    //         \"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set, but it should not be, conflict with fake collector\"\n    //     );\n    // }\n    unsafe {\n        std::env::remove_var(\"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT\"); // unsafe\n    }\n\n    opentelemetry_sdk::trace::SdkTracerProvider::builder()\n        .with_batch_exporter(\n            SpanExporter::builder()\n                .with_tonic()\n                .with_endpoint(fake_server.endpoint())\n                .build()\n                .expect(\"failed to install tracer\"),\n        )\n        .build()\n}\n\npub async fn setup_logger_provider(\n    fake_server: &FakeCollectorServer,\n) -> opentelemetry_sdk::logs::SdkLoggerProvider {\n    opentelemetry_sdk::logs::SdkLoggerProvider::builder()\n        //Install simple so we don't have to wait for batching in tests\n        .with_simple_exporter(\n            LogExporter::builder()\n                .with_tonic()\n                .with_endpoint(fake_server.endpoint())\n                .build()\n                .expect(\"failed to install logging\"),\n        )\n        .build()\n}\n\npub async fn setup_meter_provider(\n    fake_server: &FakeCollectorServer,\n) -> opentelemetry_sdk::metrics::SdkMeterProvider {\n    let exporter = MetricExporter::builder()\n        .with_tonic()\n        .with_endpoint(fake_server.endpoint())\n        .build()\n        .expect(\"failed to install metrics\");\n\n    let reader = PeriodicReader::builder(exporter).build();\n\n    SdkMeterProvider::builder().with_reader(reader).build()\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/src/logs.rs",
    "content": "use crate::common::cnv_attributes;\nuse opentelemetry_proto::tonic::collector::logs::v1::{\n    ExportLogsServiceRequest, ExportLogsServiceResponse, logs_service_server::LogsService,\n};\nuse serde::Serialize;\nuse std::collections::BTreeMap;\nuse tokio::sync::mpsc;\n\n/// This is created to flatten the log record to make it more compatible with insta for testing\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct ExportedLog {\n    pub trace_id: String,\n    pub span_id: String,\n    pub observed_time_unix_nano: u64,\n    pub severity_number: i32,\n    pub severity_text: String,\n    pub body: Option<String>,\n    pub attributes: BTreeMap<String, String>,\n    pub dropped_attributes_count: u32,\n    pub flags: u32,\n}\n\nimpl From<opentelemetry_proto::tonic::logs::v1::LogRecord> for ExportedLog {\n    fn from(value: opentelemetry_proto::tonic::logs::v1::LogRecord) -> Self {\n        Self {\n            trace_id: hex::encode(value.trace_id),\n            span_id: hex::encode(value.span_id),\n            observed_time_unix_nano: value.observed_time_unix_nano,\n            severity_number: value.severity_number,\n            severity_text: value.severity_text,\n            body: value.body.map(|value| format!(\"{value:?}\")),\n            attributes: cnv_attributes(&value.attributes),\n            dropped_attributes_count: value.dropped_attributes_count,\n            flags: value.flags,\n        }\n    }\n}\n\npub(crate) struct FakeLogsService {\n    tx: mpsc::Sender<ExportedLog>,\n}\n\nimpl FakeLogsService {\n    pub fn new(tx: mpsc::Sender<ExportedLog>) -> Self {\n        Self { tx }\n    }\n}\n\n#[tonic::async_trait]\nimpl LogsService for FakeLogsService {\n    async fn export(\n        &self,\n        request: tonic::Request<ExportLogsServiceRequest>,\n    ) -> Result<tonic::Response<ExportLogsServiceResponse>, tonic::Status> {\n        let sender = self.tx.clone();\n        for el in request\n            .into_inner()\n            .resource_logs\n            .into_iter()\n            .flat_map(|rl| rl.scope_logs)\n            .flat_map(|sl| sl.log_records)\n            .map(ExportedLog::from)\n        {\n            sender\n                .send(el)\n                .await\n                .inspect_err(|e| eprintln!(\"failed to send to channel: {e}\"))\n                .map_err(|err| tonic::Status::from_error(Box::new(err)))?;\n        }\n\n        Ok(tonic::Response::new(ExportLogsServiceResponse {\n            partial_success: None,\n        }))\n    }\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/src/metrics.rs",
    "content": "use crate::common::cnv_attributes;\nuse opentelemetry_proto::tonic::{\n    collector::metrics::v1::{\n        ExportMetricsServiceRequest, ExportMetricsServiceResponse,\n        metrics_service_server::MetricsService,\n    },\n    metrics::v1 as otel_metrics,\n};\nuse serde::Serialize;\nuse std::collections::BTreeMap;\nuse tokio::sync::mpsc;\n\npub(crate) struct FakeMetricsService {\n    tx: mpsc::Sender<ExportedMetric>,\n}\n\nimpl FakeMetricsService {\n    pub fn new(tx: mpsc::Sender<ExportedMetric>) -> Self {\n        Self { tx }\n    }\n}\n\n#[tonic::async_trait]\nimpl MetricsService for FakeMetricsService {\n    async fn export(\n        &self,\n        request: tonic::Request<ExportMetricsServiceRequest>,\n    ) -> Result<tonic::Response<ExportMetricsServiceResponse>, tonic::Status> {\n        let sender = self.tx.clone();\n        for el in request\n            .into_inner()\n            .resource_metrics\n            .iter()\n            .flat_map(|e| e.scope_metrics.to_vec())\n            .map(ExportedMetric::from)\n        {\n            sender\n                .send(el)\n                .await\n                .inspect_err(|e| eprintln!(\"failed to send to channel: {e}\"))\n                .map_err(|err| tonic::Status::from_error(Box::new(err)))?;\n        }\n\n        Ok(tonic::Response::new(ExportMetricsServiceResponse {\n            partial_success: None,\n        }))\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct ExportedMetric {\n    pub metrics: Vec<Metric>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Metric {\n    pub name: String,\n    pub description: String,\n    pub unit: String,\n    pub data: Option<MetricsData>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub enum MetricsData {\n    Gauge(Gauge),\n    Sum(Sum),\n    Histogram(Histogram),\n    ExponentialHistogram(ExponentialHistogram),\n    Summary(Summary),\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Gauge {\n    pub data_points: Vec<NumberDataPoint>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Sum {\n    pub data_points: Vec<NumberDataPoint>,\n    pub aggregation_temporality: i32,\n    pub is_monotonic: bool,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Histogram {\n    pub data_points: Vec<HistogramDataPoint>,\n    pub aggregation_temporality: i32,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct ExponentialHistogram {\n    pub data_points: Vec<ExponentialHistogramDataPoint>,\n    pub aggregation_temporality: i32,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Summary {\n    pub data_points: Vec<SummaryDataPoint>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct NumberDataPoint {\n    pub attributes: BTreeMap<String, String>,\n    pub start_time_unix_nano: u64,\n    pub time_unix_nano: u64,\n    pub exemplars: Vec<Exemplar>,\n    pub flags: u32,\n    pub value: Option<Value>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct HistogramDataPoint {\n    pub attributes: BTreeMap<String, String>,\n    pub start_time_unix_nano: u64,\n    pub time_unix_nano: u64,\n    pub count: u64,\n    pub sum: Option<f64>,\n    pub bucket_counts: Vec<u64>,\n    pub explicit_bounds: Vec<f64>,\n    pub flags: u32,\n    pub min: Option<f64>,\n    pub max: Option<f64>,\n    pub exemplars: Vec<Exemplar>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct ExponentialHistogramDataPoint {\n    pub attributes: BTreeMap<String, String>,\n    pub start_time_unix_nano: u64,\n    pub time_unix_nano: u64,\n    pub count: u64,\n    pub sum: Option<f64>,\n    pub scale: i32,\n    pub zero_count: u64,\n    pub positive: Option<Buckets>,\n    pub negative: Option<Buckets>,\n    pub flags: u32,\n    pub exemplars: Vec<Exemplar>,\n    pub min: Option<f64>,\n    pub max: Option<f64>,\n    pub zero_threshold: f64,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct Buckets {\n    pub offset: i32,\n    pub bucket_counts: Vec<u64>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct SummaryDataPoint {\n    pub attributes: BTreeMap<String, String>,\n    pub start_time_unix_nano: u64,\n    pub time_unix_nano: u64,\n    pub count: u64,\n    pub sum: f64,\n    pub quantile_values: Vec<ValueAtQuantile>,\n    pub flags: u32,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct ValueAtQuantile {\n    pub quantile: f64,\n    pub value: f64,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub struct Exemplar {\n    pub filtered_attributes: BTreeMap<String, String>,\n    pub time_unix_nano: u64,\n    pub span_id: String,\n    pub trace_id: String,\n    pub value: Option<Value>,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize)]\npub enum Value {\n    AsDouble(f64),\n    AsInt(i64),\n}\n\nimpl From<otel_metrics::ScopeMetrics> for ExportedMetric {\n    fn from(value: otel_metrics::ScopeMetrics) -> Self {\n        ExportedMetric {\n            metrics: value\n                .metrics\n                .iter()\n                .map(|m| Metric {\n                    name: m.name.clone(),\n                    description: m.description.clone(),\n                    unit: m.unit.clone(),\n                    data: m.data.clone().map(Into::into),\n                })\n                .collect(),\n        }\n    }\n}\n\nimpl From<otel_metrics::metric::Data> for MetricsData {\n    fn from(value: otel_metrics::metric::Data) -> Self {\n        match value {\n            otel_metrics::metric::Data::Gauge(g) => MetricsData::Gauge(g.into()),\n            otel_metrics::metric::Data::Sum(s) => MetricsData::Sum(s.into()),\n            otel_metrics::metric::Data::Histogram(h) => MetricsData::Histogram(h.into()),\n            otel_metrics::metric::Data::ExponentialHistogram(h) => {\n                MetricsData::ExponentialHistogram(h.into())\n            }\n            otel_metrics::metric::Data::Summary(s) => MetricsData::Summary(s.into()),\n        }\n    }\n}\n\nimpl From<otel_metrics::Summary> for Summary {\n    fn from(value: otel_metrics::Summary) -> Self {\n        Self {\n            data_points: value.data_points.iter().map(Into::into).collect(),\n        }\n    }\n}\n\nimpl From<otel_metrics::ExponentialHistogram> for ExponentialHistogram {\n    fn from(value: otel_metrics::ExponentialHistogram) -> Self {\n        Self {\n            data_points: value.data_points.iter().map(Into::into).collect(),\n            aggregation_temporality: value.aggregation_temporality,\n        }\n    }\n}\n\nimpl From<otel_metrics::Histogram> for Histogram {\n    fn from(value: otel_metrics::Histogram) -> Self {\n        Self {\n            data_points: value.data_points.iter().map(Into::into).collect(),\n            aggregation_temporality: value.aggregation_temporality,\n        }\n    }\n}\n\nimpl From<otel_metrics::Sum> for Sum {\n    fn from(value: otel_metrics::Sum) -> Self {\n        Self {\n            data_points: value.data_points.iter().map(Into::into).collect(),\n            aggregation_temporality: value.aggregation_temporality,\n            is_monotonic: value.is_monotonic,\n        }\n    }\n}\n\nimpl From<otel_metrics::Gauge> for Gauge {\n    fn from(value: otel_metrics::Gauge) -> Self {\n        Self {\n            data_points: value.data_points.iter().map(Into::into).collect(),\n        }\n    }\n}\n\nimpl From<&otel_metrics::NumberDataPoint> for NumberDataPoint {\n    fn from(value: &otel_metrics::NumberDataPoint) -> Self {\n        Self {\n            attributes: cnv_attributes(&value.attributes),\n            start_time_unix_nano: value.start_time_unix_nano,\n            time_unix_nano: value.time_unix_nano,\n            exemplars: value.exemplars.iter().map(Into::into).collect(),\n            flags: value.flags,\n            value: value.value.map(Into::into),\n        }\n    }\n}\n\nimpl From<&otel_metrics::Exemplar> for Exemplar {\n    fn from(value: &otel_metrics::Exemplar) -> Self {\n        Self {\n            filtered_attributes: cnv_attributes(&value.filtered_attributes),\n            time_unix_nano: value.time_unix_nano,\n            span_id: hex::encode(&value.span_id),\n            trace_id: hex::encode(&value.trace_id),\n            value: value.value.map(Into::into),\n        }\n    }\n}\n\nimpl From<&otel_metrics::SummaryDataPoint> for SummaryDataPoint {\n    fn from(value: &otel_metrics::SummaryDataPoint) -> Self {\n        Self {\n            attributes: cnv_attributes(&value.attributes),\n            start_time_unix_nano: value.start_time_unix_nano,\n            time_unix_nano: value.time_unix_nano,\n            count: value.count,\n            sum: value.sum,\n            quantile_values: value.quantile_values.iter().map(Into::into).collect(),\n            flags: value.flags,\n        }\n    }\n}\n\nimpl From<&otel_metrics::summary_data_point::ValueAtQuantile> for ValueAtQuantile {\n    fn from(value: &otel_metrics::summary_data_point::ValueAtQuantile) -> Self {\n        Self {\n            quantile: value.quantile,\n            value: value.value,\n        }\n    }\n}\n\nimpl From<&otel_metrics::HistogramDataPoint> for HistogramDataPoint {\n    fn from(value: &otel_metrics::HistogramDataPoint) -> Self {\n        Self {\n            attributes: cnv_attributes(&value.attributes),\n            start_time_unix_nano: value.start_time_unix_nano,\n            time_unix_nano: value.time_unix_nano,\n            count: value.count,\n            sum: value.sum,\n            bucket_counts: value.bucket_counts.to_vec(),\n            flags: value.flags,\n            explicit_bounds: value.explicit_bounds.to_vec(),\n            max: value.max,\n            min: value.min,\n            exemplars: value.exemplars.iter().map(Into::into).collect(),\n        }\n    }\n}\n\nimpl From<&otel_metrics::ExponentialHistogramDataPoint> for ExponentialHistogramDataPoint {\n    fn from(value: &otel_metrics::ExponentialHistogramDataPoint) -> Self {\n        Self {\n            attributes: cnv_attributes(&value.attributes),\n            start_time_unix_nano: value.start_time_unix_nano,\n            time_unix_nano: value.time_unix_nano,\n            count: value.count,\n            sum: value.sum,\n            scale: value.scale,\n            zero_count: value.zero_count,\n            positive: value.positive.as_ref().map(Into::into),\n            negative: value.negative.as_ref().map(Into::into),\n            flags: value.flags,\n            exemplars: value.exemplars.iter().map(Into::into).collect(),\n            max: value.max,\n            min: value.min,\n            zero_threshold: value.zero_threshold,\n        }\n    }\n}\n\nimpl From<&otel_metrics::exponential_histogram_data_point::Buckets> for Buckets {\n    fn from(value: &otel_metrics::exponential_histogram_data_point::Buckets) -> Self {\n        Self {\n            offset: value.offset,\n            bucket_counts: value.bucket_counts.to_vec(),\n        }\n    }\n}\n\nimpl From<otel_metrics::exemplar::Value> for Value {\n    fn from(value: otel_metrics::exemplar::Value) -> Self {\n        match value {\n            otel_metrics::exemplar::Value::AsDouble(n) => Value::AsDouble(n),\n            otel_metrics::exemplar::Value::AsInt(n) => Value::AsInt(n),\n        }\n    }\n}\n\nimpl From<otel_metrics::number_data_point::Value> for Value {\n    fn from(value: otel_metrics::number_data_point::Value) -> Self {\n        match value {\n            otel_metrics::number_data_point::Value::AsDouble(n) => Value::AsDouble(n),\n            otel_metrics::number_data_point::Value::AsInt(n) => Value::AsInt(n),\n        }\n    }\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/src/trace.rs",
    "content": "//! based on https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry-otlp/tests/smoke.rs\nuse crate::common::cnv_attributes;\nuse opentelemetry_proto::tonic::collector::trace::v1::{\n    ExportTraceServiceRequest, ExportTraceServiceResponse, trace_service_server::TraceService,\n};\nuse serde::Serialize;\nuse std::collections::BTreeMap;\nuse tokio::sync::mpsc;\n\nuse tracing::debug;\n\n/// opentelemetry_proto::tonic::trace::v1::Span is no compatible with serde::Serialize\n/// and to be able to test with insta,... it's needed (Debug is not enough to be able to filter unstable value,...)\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct ExportedSpan {\n    pub trace_id: String,\n    pub span_id: String,\n    pub trace_state: String,\n    pub parent_span_id: String,\n    pub name: String,\n    pub kind: String, //SpanKind,\n    pub start_time_unix_nano: u64,\n    pub end_time_unix_nano: u64,\n    pub attributes: BTreeMap<String, String>,\n    pub dropped_attributes_count: u32,\n    pub events: Vec<Event>,\n    pub dropped_events_count: u32,\n    pub links: Vec<Link>,\n    pub dropped_links_count: u32,\n    pub status: Option<Status>,\n}\n\nimpl From<opentelemetry_proto::tonic::trace::v1::Span> for ExportedSpan {\n    fn from(value: opentelemetry_proto::tonic::trace::v1::Span) -> Self {\n        Self {\n            trace_id: hex::encode(&value.trace_id),\n            span_id: hex::encode(&value.span_id),\n            trace_state: value.trace_state.clone(),\n            parent_span_id: hex::encode(&value.parent_span_id),\n            name: value.name.clone(),\n            kind: value.kind().as_str_name().to_owned(),\n            start_time_unix_nano: value.start_time_unix_nano,\n            end_time_unix_nano: value.end_time_unix_nano,\n            attributes: cnv_attributes(&value.attributes),\n            dropped_attributes_count: value.dropped_attributes_count,\n            events: value.events.iter().map(Event::from).collect(),\n            dropped_events_count: value.dropped_events_count,\n            links: value.links.iter().map(Link::from).collect(),\n            dropped_links_count: value.dropped_links_count,\n            status: value.status.map(Status::from),\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize)]\npub struct Status {\n    pub message: String,\n    pub code: String,\n}\n\nimpl From<opentelemetry_proto::tonic::trace::v1::Status> for Status {\n    fn from(value: opentelemetry_proto::tonic::trace::v1::Status) -> Self {\n        Self {\n            message: value.message.clone(),\n            code: value.code().as_str_name().to_string(),\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct Link {\n    pub trace_id: String,\n    pub span_id: String,\n    pub trace_state: String,\n    pub attributes: BTreeMap<String, String>,\n    pub dropped_attributes_count: u32,\n}\n\nimpl From<&opentelemetry_proto::tonic::trace::v1::span::Link> for Link {\n    fn from(value: &opentelemetry_proto::tonic::trace::v1::span::Link) -> Self {\n        Self {\n            trace_id: hex::encode(&value.trace_id),\n            span_id: hex::encode(&value.span_id),\n            trace_state: value.trace_state.clone(),\n            attributes: cnv_attributes(&value.attributes),\n            dropped_attributes_count: value.dropped_attributes_count,\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize)]\npub struct Event {\n    pub time_unix_nano: u64,\n    pub name: String,\n    pub attributes: BTreeMap<String, String>,\n    pub dropped_attributes_count: u32,\n}\n\nimpl From<&opentelemetry_proto::tonic::trace::v1::span::Event> for Event {\n    fn from(value: &opentelemetry_proto::tonic::trace::v1::span::Event) -> Self {\n        Self {\n            time_unix_nano: value.time_unix_nano,\n            name: value.name.clone(),\n            attributes: cnv_attributes(&value.attributes),\n            dropped_attributes_count: value.dropped_attributes_count,\n        }\n    }\n}\n\npub(crate) struct FakeTraceService {\n    tx: mpsc::Sender<ExportedSpan>,\n}\n\nimpl FakeTraceService {\n    pub fn new(tx: mpsc::Sender<ExportedSpan>) -> Self {\n        Self { tx }\n    }\n}\n\n#[tonic::async_trait]\nimpl TraceService for FakeTraceService {\n    async fn export(\n        &self,\n        request: tonic::Request<ExportTraceServiceRequest>,\n    ) -> Result<tonic::Response<ExportTraceServiceResponse>, tonic::Status> {\n        debug!(\"Sending request into channel...\");\n        let sender = self.tx.clone();\n        for es in request\n            .into_inner()\n            .resource_spans\n            .into_iter()\n            .flat_map(|rs| rs.scope_spans)\n            .flat_map(|ss| ss.spans)\n            .map(ExportedSpan::from)\n        {\n            sender\n                .send(es)\n                .await\n                .inspect_err(|e| eprintln!(\"failed to send to channel: {e}\"))\n                .map_err(|err| tonic::Status::from_error(Box::new(err)))?;\n        }\n        Ok(tonic::Response::new(ExportTraceServiceResponse {\n            partial_success: None,\n        }))\n    }\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/demo_log.rs",
    "content": "use std::time::Duration;\n\nuse fake_opentelemetry_collector::{FakeCollectorServer, setup_logger_provider};\nuse opentelemetry::logs::{LogRecord, Logger, LoggerProvider, Severity};\nuse tracing::debug;\n\n#[tokio::test(flavor = \"multi_thread\")]\nasync fn demo_fake_logger_and_collector() {\n    debug!(\"Start the fake collector\");\n    let mut fake_collector = FakeCollectorServer::start()\n        .await\n        .expect(\"fake collector setup and started\");\n\n    debug!(\"Init the 'application' & logger provider\");\n    let logger_provider = setup_logger_provider(&fake_collector).await;\n    let logger = logger_provider.logger(\"test\");\n\n    debug!(\"Run the 'application' & send log ...\");\n    let mut record = logger.create_log_record();\n    record.set_body(\"This is information\".into());\n    record.set_severity_number(Severity::Info);\n    record.set_severity_text(\"info\");\n    logger.emit(record);\n\n    debug!(\"Shutdown the 'application' & logger provider\");\n    let _ = logger_provider.force_flush();\n    logger_provider\n        .shutdown()\n        .expect(\"no error during shutdown\");\n    drop(logger_provider);\n\n    debug!(\"Collect & check the logs\");\n    let otel_logs = fake_collector\n        .exported_logs(1, Duration::from_millis(500))\n        .await;\n\n    insta::assert_yaml_snapshot!(otel_logs, {\n        \"[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", trace_id.len())\n        }),\n        \"[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].observed_time_unix_nano\" => \"[timestamp]\",\n        \"[].severity_number\" => 9,\n        \"[].severity_text\" => \"info\",\n        \"[].body\" => \"AnyValue { value: Some(StringValue(\\\"This is information\\\")) }\",\n    });\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/demo_metrics.rs",
    "content": "use fake_opentelemetry_collector::{FakeCollectorServer, setup_meter_provider};\nuse opentelemetry::{KeyValue, global};\nuse std::time::Duration;\nuse tracing::debug;\n\n#[tokio::test(flavor = \"multi_thread\")]\nasync fn demo_fake_meter_and_collector() {\n    debug!(\"Start the fake collector\");\n    let mut fake_collector = FakeCollectorServer::start()\n        .await\n        .expect(\"fake collector setup and started\");\n\n    debug!(\"Init the 'application' & meter provider\");\n    let meter_provider = setup_meter_provider(&fake_collector).await;\n    global::set_meter_provider(meter_provider.clone());\n\n    debug!(\"Run the 'application' & send metrics ...\");\n    let meter = global::meter(\"test\");\n    let attributes = &[KeyValue::new(\"foo\", \"bar\")];\n\n    let gauge = meter\n        .f64_gauge(\"test_gauge\")\n        .with_description(\"A test gauge\")\n        .with_unit(\"km/s\")\n        .build();\n    gauge.record(123.456, attributes);\n\n    let up_down_counter = meter\n        .i64_up_down_counter(\"test_updown_counter\")\n        .with_description(\"A test up-down-counter\")\n        .with_unit(\"m/s^2\")\n        .build();\n    up_down_counter.add(-50, attributes);\n\n    let counter = meter\n        .u64_counter(\"test_counter\")\n        .with_description(\"A test counter\")\n        .with_unit(\"Jigawatts\")\n        .build();\n    counter.add(25, attributes);\n\n    let histogram = meter\n        .u64_histogram(\"test_histogram\")\n        .with_description(\"A test histogram\")\n        .with_unit(\"ft/in^2\")\n        .build();\n    histogram.record(10, attributes);\n    histogram.record(13, attributes);\n\n    debug!(\"Shutdown the 'application' & meter provider\");\n    meter_provider.shutdown().expect(\"no error during shutdown\");\n    drop(meter_provider);\n\n    debug!(\"Collect & check the metrics\");\n    let otel_metrics = fake_collector\n        .exported_metrics(1, Duration::from_millis(500))\n        .await;\n\n    insta::assert_yaml_snapshot!(otel_metrics, {\n        // Validate gauge metric\n        \"[0].metrics[0].name\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(name) = value.as_str());\n            assert_eq!(name, \"test_gauge\");\n            name.to_string()\n        }),\n        \"[0].metrics[0].description\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(desc) = value.as_str());\n            assert_eq!(desc, \"A test gauge\");\n            desc.to_string()\n        }),\n        \"[0].metrics[0].unit\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(unit) = value.as_str());\n            assert_eq!(unit, \"km/s\");\n            unit.to_string()\n        }),\n        \"[0].metrics[0].data.Gauge.data_points[0].value.AsDouble\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(val) = value.as_f64());\n            assert!((val - 123.456).abs() < 0.001);\n            format!(\"{val}\")\n        }),\n\n        // Validate up-down counter\n        \"[0].metrics[1].name\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(name) = value.as_str());\n            assert_eq!(name, \"test_updown_counter\");\n            name.to_string()\n        }),\n        \"[0].metrics[1].description\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(desc) = value.as_str());\n            assert_eq!(desc, \"A test up-down-counter\");\n            desc.to_string()\n        }),\n        \"[0].metrics[1].unit\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(unit) = value.as_str());\n            assert_eq!(unit, \"m/s^2\");\n            unit.to_string()\n        }),\n        \"[0].metrics[1].data.Sum.data_points[0].value.AsInt\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(val) = value.as_i64());\n            assert_eq!(val, -50);\n            format!(\"{val}\")\n        }),\n        \"[0].metrics[1].data.Sum.is_monotonic\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(monotonic) = value.as_bool());\n            assert!(!monotonic);\n            format!(\"{monotonic}\")\n        }),\n        \"[0].metrics[1].data.Sum.aggregation_temporality\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(temporality) = value.as_u64());\n            assert_eq!(temporality, 2); // Cumulative\n            format!(\"{temporality}\")\n        }),\n\n        // Validate counter\n        \"[0].metrics[2].name\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(name) = value.as_str());\n            assert_eq!(name, \"test_counter\");\n            name.to_string()\n        }),\n        \"[0].metrics[2].description\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(desc) = value.as_str());\n            assert_eq!(desc, \"A test counter\");\n            desc.to_string()\n        }),\n        \"[0].metrics[2].unit\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(unit) = value.as_str());\n            assert_eq!(unit, \"Jigawatts\");\n            unit.to_string()\n        }),\n        \"[0].metrics[2].data.Sum.data_points[0].value.AsInt\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(val) = value.as_i64());\n            assert_eq!(val, 25);\n            format!(\"{val}\")\n        }),\n        \"[0].metrics[2].data.Sum.is_monotonic\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(monotonic) = value.as_bool());\n            assert!(monotonic);\n            format!(\"{monotonic}\")\n        }),\n        \"[0].metrics[2].data.Sum.aggregation_temporality\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(temporality) = value.as_u64());\n            assert_eq!(temporality, 2); // Cumulative\n            format!(\"{temporality}\")\n        }),\n\n        // Validate histogram\n        \"[0].metrics[3].name\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(name) = value.as_str());\n            assert_eq!(name, \"test_histogram\");\n            name.to_string()\n        }),\n        \"[0].metrics[3].description\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(desc) = value.as_str());\n            assert_eq!(desc, \"A test histogram\");\n            desc.to_string()\n        }),\n        \"[0].metrics[3].unit\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(unit) = value.as_str());\n            assert_eq!(unit, \"ft/in^2\");\n            unit.to_string()\n        }),\n        \"[0].metrics[3].data.Histogram.data_points[0].count\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(count) = value.as_u64());\n            assert_eq!(count, 2);\n            format!(\"{count}\")\n        }),\n        \"[0].metrics[3].data.Histogram.data_points[0].sum\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(sum) = value.as_u64());\n            assert_eq!(sum, 23); // 10 + 13\n            format!(\"{sum}\")\n        }),\n        \"[0].metrics[3].data.Histogram.data_points[0].min\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(min) = value.as_u64());\n            assert_eq!(min, 10);\n            format!(\"{min}\")\n        }),\n        \"[0].metrics[3].data.Histogram.data_points[0].max\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(max) = value.as_u64());\n            assert_eq!(max, 13);\n            format!(\"{max}\")\n        }),\n        \"[0].metrics[3].data.Histogram.aggregation_temporality\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(temporality) = value.as_u64());\n            assert_eq!(temporality, 2); // Cumulative\n            format!(\"{temporality}\")\n        }),\n\n        // Validate attributes for all metrics\n        \"[].metrics[].data.**.attributes.foo\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(attr_value) = value.as_str());\n            assert!(attr_value.contains(\"bar\"));\n            \"\\\"Some(AnyValue { value: Some(StringValue(\\\\\\\"bar\\\\\\\")) })\\\"\"\n        }),\n\n        // Redact timestamps\n        \"[].metrics[].data.**.start_time_unix_nano\" => \"[timestamp]\",\n        \"[].metrics[].data.**.time_unix_nano\" => \"[timestamp]\",\n        \"[].metrics[].data.**.exemplars[].time_unix_nano\" => \"[timestamp]\",\n    });\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/demo_trace.rs",
    "content": "use std::time::Duration;\n\nuse fake_opentelemetry_collector::{FakeCollectorServer, setup_tracer_provider};\nuse opentelemetry::trace::TracerProvider;\nuse opentelemetry::trace::{Span, SpanKind, Tracer};\nuse tracing::debug;\n\n#[tokio::test(flavor = \"multi_thread\")]\nasync fn demo_fake_tracer_and_collector() {\n    debug!(\"Start the fake collector\");\n    let mut fake_collector = FakeCollectorServer::start()\n        .await\n        .expect(\"fake collector setup and started\");\n\n    debug!(\"Init the 'application' & tracer provider\");\n    let tracer_provider = setup_tracer_provider(&fake_collector).await;\n    let tracer = tracer_provider.tracer(\"test\");\n\n    debug!(\"Run the 'application' & sending span...\");\n    let mut span = tracer\n        .span_builder(\"my-test-span\")\n        .with_kind(SpanKind::Server)\n        .start(&tracer);\n    span.add_event(\"my-test-event\", vec![]);\n    span.end();\n\n    debug!(\"Shutdown the 'application' & tracer provider and force flush the spans\");\n    let _ = tracer_provider.force_flush();\n    tracer_provider\n        .shutdown()\n        .expect(\"no error during shutdown\");\n    drop(tracer_provider);\n\n    debug!(\"Collect & check the spans\");\n    let otel_spans = fake_collector\n        .exported_spans(1, Duration::from_secs(20))\n        .await;\n    //insta::assert_debug_snapshot!(otel_spans);\n    insta::assert_yaml_snapshot!(otel_spans, {\n        \"[].start_time_unix_nano\" => \"[timestamp]\",\n        \"[].end_time_unix_nano\" => \"[timestamp]\",\n        \"[].events[].time_unix_nano\" => \"[timestamp]\",\n        \"[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", trace_id.len())\n        }),\n        \"[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].links[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", trace_id.len())\n        }),\n        \"[].links[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert2::assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n    });\n}\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/snapshots/demo_log__demo_fake_logger_and_collector.snap",
    "content": "---\nsource: fake-opentelemetry-collector/tests/demo_log.rs\nexpression: otel_logs\nsnapshot_kind: text\n---\n- trace_id: \"[trace_id:lg0]\"\n  span_id: \"[span_id:lg0]\"\n  observed_time_unix_nano: \"[timestamp]\"\n  severity_number: 9\n  severity_text: info\n  body: \"AnyValue { value: Some(StringValue(\\\"This is information\\\")) }\"\n  attributes: {}\n  dropped_attributes_count: 0\n  flags: 0\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/snapshots/demo_metrics__demo_fake_meter_and_collector.snap",
    "content": "---\nsource: fake-opentelemetry-collector/tests/demo_metrics.rs\nexpression: otel_metrics\n---\n- metrics:\n    - name: test_gauge\n      description: A test gauge\n      unit: km/s\n      data:\n        Gauge:\n          data_points:\n            - attributes:\n                foo: \"\\\"Some(AnyValue { value: Some(StringValue(\\\\\\\"bar\\\\\\\")) })\\\"\"\n              start_time_unix_nano: \"[timestamp]\"\n              time_unix_nano: \"[timestamp]\"\n              exemplars: []\n              flags: 0\n              value:\n                AsDouble: 123.456\n    - name: test_updown_counter\n      description: A test up-down-counter\n      unit: m/s^2\n      data:\n        Sum:\n          data_points:\n            - attributes:\n                foo: \"\\\"Some(AnyValue { value: Some(StringValue(\\\\\\\"bar\\\\\\\")) })\\\"\"\n              start_time_unix_nano: \"[timestamp]\"\n              time_unix_nano: \"[timestamp]\"\n              exemplars: []\n              flags: 0\n              value:\n                AsInt: -50\n          aggregation_temporality: 2\n          is_monotonic: false\n    - name: test_counter\n      description: A test counter\n      unit: Jigawatts\n      data:\n        Sum:\n          data_points:\n            - attributes:\n                foo: \"\\\"Some(AnyValue { value: Some(StringValue(\\\\\\\"bar\\\\\\\")) })\\\"\"\n              start_time_unix_nano: \"[timestamp]\"\n              time_unix_nano: \"[timestamp]\"\n              exemplars: []\n              flags: 0\n              value:\n                AsInt: 25\n          aggregation_temporality: 2\n          is_monotonic: true\n    - name: test_histogram\n      description: A test histogram\n      unit: ft/in^2\n      data:\n        Histogram:\n          data_points:\n            - attributes:\n                foo: \"\\\"Some(AnyValue { value: Some(StringValue(\\\\\\\"bar\\\\\\\")) })\\\"\"\n              start_time_unix_nano: \"[timestamp]\"\n              time_unix_nano: \"[timestamp]\"\n              count: 2\n              sum: 23\n              bucket_counts:\n                - 0\n                - 0\n                - 1\n                - 1\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n                - 0\n              explicit_bounds:\n                - 0\n                - 5\n                - 10\n                - 25\n                - 50\n                - 75\n                - 100\n                - 250\n                - 500\n                - 750\n                - 1000\n                - 2500\n                - 5000\n                - 7500\n                - 10000\n              flags: 0\n              min: 10\n              max: 13\n              exemplars: []\n          aggregation_temporality: 2\n"
  },
  {
    "path": "fake-opentelemetry-collector/tests/snapshots/demo_trace__demo_fake_tracer_and_collector.snap",
    "content": "---\nsource: fake-opentelemetry-collector/tests/demo_trace.rs\nexpression: otel_spans\nsnapshot_kind: text\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"\"\n  name: my-test-span\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes: {}\n  dropped_attributes_count: 0\n  events:\n    - time_unix_nano: \"[timestamp]\"\n      name: my-test-event\n      attributes: {}\n      dropped_attributes_count: 0\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "init-tracing-opentelemetry/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.37.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.36.1...init-tracing-opentelemetry-v0.37.0) - 2026-04-27\n\n### <!-- 1 -->Fixed\n\n- *(init)* metrics enabled if feature flages & explicit enabling\n- avoid to log sensitive OTEL value, and warn if issue on logfmt\n- *(docs)* build of sample & add screenshot for log\n\n### <!-- 2 -->Added\n\n- accept owned string for service'name and version\n- add OTLP log exporter ([#333](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/333))\n\n## [0.37.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.36.1...init-tracing-opentelemetry-v0.37.0) - 2026-04-27\n\n### <!-- 1 -->Fixed\n\n- *(init)* metrics enabled if feature flages & explicit enabling\n- avoid to log sensitive OTEL value, and warn if issue on logfmt\n- *(docs)* build of sample & add screenshot for log\n\n### <!-- 2 -->Added\n\n- accept owned string for service'name and version\n- add OTLP log exporter ([#333](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/333))\n\n## [0.36.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.36.0...init-tracing-opentelemetry-v0.36.1) - 2026-03-15\n\n### <!-- 1 -->Fixed\n\n- MSRV (bump to 1.88) & api changes in dependencies, reformat ([#325](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/325))\n\n## [0.36.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.36.0...init-tracing-opentelemetry-v0.36.1) - 2026-03-15\n\n### <!-- 1 -->Fixed\n\n- MSRV (bump to 1.88) & api changes in dependencies, reformat ([#325](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/325))\n\n## [0.36.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/releases/tag/init-tracing-opentelemetry-v0.36.0) - 2026-01-19\n\n### <!-- 2 -->Added\n\n- *(deps)* update to rust 1.87 & edition 2024 ([#317](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/317))\n\n## [0.36.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.35.0...init-tracing-opentelemetry-v0.36.0) - 2026-01-19\n\n### <!-- 2 -->Added\n\n- *(deps)* update to rust 1.87 & edition 2024 ([#317](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/317))\n\n## [0.35.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.34.0...init-tracing-opentelemetry-v0.35.0) - 2026-01-12\n\n### <!-- 2 -->Added\n\n- allow to define otel tracer's name via `TracerConfig` ([#313](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/313))\n\n## [0.34.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.33.0...init-tracing-opentelemetry-v0.34.0) - 2025-11-13\n\n### <!-- 1 -->Fixed\n\n- *(init-tracing-opentelemetry)* apply custom resource configuration to OpenTelemetry layers ([#297](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/297))\n- Fix timers, booleans add support for `Layer::without_time` ([#295](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/295))\n\n### <!-- 2 -->Added\n\n- Add support for `Layer::with_thread_ids`\n- Add support for `Layer::with_file`\n- add support for `tracing_subscriber::fmt::format::Full` ([#291](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/291))\n- add in features to docs.rs rendered content. ([#287](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/287))\n\n## [0.33.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.32.1...init-tracing-opentelemetry-v0.33.0) - 2025-11-02\n\n### <!-- 2 -->Added\n\n- allow to customize the tracing configuration (registry) with additional layers ([#281](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/281))\n\n## [0.31.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.30.1...init-tracing-opentelemetry-v0.31.0) - 2025-09-27\n\n### <!-- 2 -->Added\n\n- [**breaking**] Guard struct allow future evolution, init_subscriber can be used for non global (like test,...)\n- a more configurable tracing configuration with `TracingConfig`\n\n## [0.30.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.29.0...init-tracing-opentelemetry-v0.30.0) - 2025-07-18\n\n### <!-- 2 -->Added\n\n- add support for Opentelemetry Metrics (#249)\n\n## [0.28.2](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.28.1...init-tracing-opentelemetry-v0.28.2) - 2025-06-03\n\n### <!-- 1 -->Fixed\n\n- *(deps)* missing `tonic` dependency on `tls`\n\n### <!-- 3 -->Removed\n\n- remove deprecated sample from README\n\n## [0.28.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.27.1...init-tracing-opentelemetry-v0.28.0) - 2025-03-31\n\n### <!-- 2 -->Added\n\n- *(deps)* update opentelemetry to 0.29 (#227)\n\n## [0.27.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.27.0...init-tracing-opentelemetry-v0.27.1) - 2025-02-26\n\n### <!-- 1 -->Fixed\n\n- reqwest must use blocking client since opentelemetry 0.28 (#220)\n\n### <!-- 3 -->Removed\n\n- *(deps)* remove minor constraint when major > 1\n\n## [0.27.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.26.0...init-tracing-opentelemetry-v0.27.0) - 2025-02-24\n\n### <!-- 1 -->Fixed\n\n- drop on the TracingGuard also shutdown the wrapped TracerProvider\n\n### <!-- 2 -->Added\n\n- allow to provide log's \"directives\" via `init_subscribers_and_loglevel`\n\n## [0.25.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.24.1...init-tracing-opentelemetry-v0.25.0) - 2024-12-10\n\n### <!-- 1 -->Fixed\n\n- inference of `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` for protocol `http/protobuf`\n\n## [0.24.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.24.0...init-tracing-opentelemetry-v0.24.1) - 2024-11-24\n\n### <!-- 1 -->Fixed\n\n- Use guard pattern to allow consumers to ensure final trace is sent ([#185](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/185))\n\n## [0.24.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.21.0...init-tracing-opentelemetry-v0.24.0) - 2024-09-23\n\n### <!-- 2 -->Added\n\n- [**breaking**] remove trace_id and span_id from logfmt (to avoid link with old version)\n\n## [0.21.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.20.0...init-tracing-opentelemetry-v0.21.0) - 2024-09-22\n\n### <!-- 2 -->Added\n\n- *(deps)* upgrade to opentelemetry 0.25\n- add a troubleshot section\n- [**breaking**] disable resourcedetector (os,...) until update for new version of opentelemetry\n- [**breaking**] disable support of xray (until update for new version of opentelemetry)\n\n## [0.20.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.19.0...init-tracing-opentelemetry-v0.20.0) - 2024-08-31\n\n### <!-- 1 -->Fixed\n- 🐛 fix build of contributions (upgrade of opentelemetry, fake collector for logs,...)\n\n### <!-- 4 -->Changed\n- ⬆️ upgrade to rstest 0.22\n- ⬆️ upgrade to opentelemetry 0.24 (and related dependencies) ([#151](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/151))\n\n## [0.17.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/init-tracing-opentelemetry-v0.17.0...init-tracing-opentelemetry-v0.17.1) - 2024-02-24\n\n### Other\n- 👷 tune release-plz\n"
  },
  {
    "path": "init-tracing-opentelemetry/Cargo.toml",
    "content": "[package]\nname = \"init-tracing-opentelemetry\"\ndescription = \"A set of helpers to initialize (and more) tracing + opentelemetry (compose your own or use opinionated preset)\"\nreadme = \"README.md\"\nkeywords = [\"tracing\", \"opentelemetry\"]\ncategories = [\"development-tools::debugging\", \"development-tools::profiling\"]\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/init-tracing-opentelemetry\"\nedition.workspace = true\nversion = \"0.37.0\"\nrepository.workspace = true\nlicense.workspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docs_rs\"]\n\n[dependencies]\nopentelemetry = { workspace = true }\nopentelemetry-appender-tracing = { version = \"0.31\", default-features = false, optional = true }\nopentelemetry-aws = { workspace = true, optional = true, features = [\"trace\"] }\nopentelemetry-jaeger-propagator = { workspace = true, optional = true }\nopentelemetry-otlp = { workspace = true, optional = true, features = [\n  \"grpc-tonic\",\n  \"trace\",\n] }\n# opentelemetry-resource-detectors = { workspace = true } //FIXME enable when available for opentelemetry >= 0.25\nopentelemetry-stdout = { workspace = true, features = [\n  \"trace\",\n], optional = true }\nopentelemetry-semantic-conventions = { workspace = true, optional = true }\nopentelemetry-zipkin = { workspace = true, features = [], optional = true }\nopentelemetry_sdk = { workspace = true, features = [\"trace\"] }\nthiserror = \"2\"\ntonic = { workspace = true, optional = true }\ntracing = { workspace = true }\ntracing-logfmt = { version = \"0.3\", optional = true }\ntracing-opentelemetry = { workspace = true }\ntracing-subscriber = { version = \"0.3\", default-features = false, features = [\n  \"ansi\",\n  \"env-filter\",\n  \"fmt\",\n  \"json\",\n], optional = true }\n\n[dev-dependencies]\nassert2 = { workspace = true }\nrstest = { workspace = true }\n# need tokio runtime to run smoke tests and rust doc\ntracing-opentelemetry-instrumentation-sdk = { path = \"../tracing-opentelemetry-instrumentation-sdk\" }\nopentelemetry_sdk = { workspace = true, features = [\n  \"trace\",\n  \"rt-tokio\",\n  \"testing\",\n] }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\ntokio = { version = \"1\", features = [\"full\"] }\ntokio-blocked = \"0.1\"\ntokio-stream = { version = \"0.1\" }\ntracing-subscriber = { version = \"0.3\", default-features = false, features = [\n  \"env-filter\",\n  \"fmt\",\n  \"json\",\n] }\n\n[features]\njaeger = [\"dep:opentelemetry-jaeger-propagator\"]\notlp = [\n  \"opentelemetry-otlp/http-proto\",\n  \"opentelemetry-otlp/reqwest-blocking-client\",\n  \"opentelemetry-otlp/reqwest-rustls\",\n  \"tracer\",\n]\nstdout = [\"dep:opentelemetry-stdout\", \"tracer\"]\ntracer = [\"dep:opentelemetry-semantic-conventions\"]\nxray = [\"dep:opentelemetry-aws\"]\nzipkin = [\"dep:opentelemetry-zipkin\"]\ntracing_subscriber_ext = [\"dep:tracing-subscriber\", \"otlp\"]\ntls = [\"opentelemetry-otlp/tls\", \"tonic\"]\ntls-roots = [\"opentelemetry-otlp/tls-roots\"]\ntls-webpki-roots = [\"opentelemetry-otlp/tls-webpki-roots\"]\nlogfmt = [\"dep:tracing-logfmt\"]\nlogs = [\n  \"dep:opentelemetry-appender-tracing\",\n  \"opentelemetry-otlp/logs\",\n  \"opentelemetry_sdk/logs\",\n  \"opentelemetry/logs\",\n]\nmetrics = [\n  \"opentelemetry-otlp/metrics\",\n  \"tracing-opentelemetry/metrics\",\n  \"opentelemetry-stdout/metrics\",\n]\n\n[lints]\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(docs_rs)'] }\n"
  },
  {
    "path": "init-tracing-opentelemetry/README.md",
    "content": "# init-tracing-opentelemetry\n\n[![crates license](https://img.shields.io/crates/l/init-tracing-opentelemetry.svg)](http://creativecommons.org/publicdomain/zero/1.0/)\n[![crate version](https://img.shields.io/crates/v/init-tracing-opentelemetry.svg)](https://crates.io/crates/init-tracing-opentelemetry)\n\nA set of helpers to initialize (and more) tracing + opentelemetry (compose your own or use opinionated preset)\n\n```rust\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // Simple preset\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n\n    //...\n\n    Ok(())\n}\n```\n\n```rust\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // custom configuration\n    let _guard = init_tracing_opentelemetry::TracingConfig::default()\n        .with_json_format()\n        .with_stderr()\n        .with_log_directives(\"debug\")\n        .init_subscriber()?;\n\n    //...\n\n    Ok(())\n}\n```\n\nThe `init_subscriber()` function returns an `OtelGuard` instance. Following the guard pattern, this struct provides no functions but, when dropped, ensures that any pending traces/metrics are sent before it exits. The syntax `let _guard` is suggested to ensure that Rust does not drop the struct until the application exits.\n\n## Configuration Options\n\n### Presets\n\n- `TracingConfig::development()` - Pretty format, stderr, with debug info\n- `TracingConfig::production()` - JSON format, stdout, minimal metadata\n- `TracingConfig::debug()` - Full verbosity with all span events\n- `TracingConfig::minimal()` - Compact format, no OpenTelemetry\n- `TracingConfig::testing()` - Minimal output for tests\n\n### Custom Configuration\n\n```rust,no_run\nuse init_tracing_opentelemetry::TracingConfig;\n\nTracingConfig::default()\n    .with_pretty_format()           // or .with_json_format(), .with_compact_format()\n    .with_stderr()                  // or .with_stdout(), .with_file(path)\n    .with_log_directives(\"debug\")   // Custom log levels\n    .with_line_numbers(true)        // Include line numbers\n    .with_thread_names(true)        // Include thread names\n    .with_otel(true)                // Enable OpenTelemetry\n    .init_subscriber()\n    .expect(\"valid tracing configuration\");\n```\n\n### Add custom layer, modify subscriber\n\nUse `init_subscriber_ext(|subscriber| {...} )` to transform the subscriber (registry), before application of the configuration.\n\n```rust\nuse init_tracing_opentelemetry::TracingConfig;\nuse tokio_blocked::TokioBlockedLayer;\nuse tracing::info;\nuse tracing_subscriber::layer::SubscriberExt;\n\n#[tokio::main]\nasync fn main() {\n    let blocked = TokioBlockedLayer::new()\n        .with_warn_busy_single_poll(Some(std::time::Duration::from_micros(150)));\n\n    let _guard = TracingConfig::default()\n        .with_log_directives(\"info,tokio::task=trace,tokio::task::waker=warn\")\n        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::NONE)\n        .init_subscriber_ext(|subscriber| subscriber.with(blocked))\n        .unwrap();\n\n    tokio::task::spawn(async {\n        // BAD!\n        // This produces a warning log message.\n        info!(\"blocking!\");\n        std::thread::sleep(std::time::Duration::from_secs(1));\n    })\n    .await\n    .unwrap();\n\n    tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;\n}\n```\n\n### Legacy API (deprecated)\n\nFor backward compatibility, the old API is still available:\n\n```txt\npub fn build_loglevel_filter_layer() -> tracing_subscriber::filter::EnvFilter {\n    // filter what is output on log (fmt)\n    // std::env::set_var(\"RUST_LOG\", \"warn,axum_tracing_opentelemetry=info,otel=debug\");\n    std::env::set_var(\n        \"RUST_LOG\",\n        format!(\n            // `otel::tracing` should be a level trace to emit opentelemetry trace & span\n            // `otel::setup` set to debug to log detected resources, configuration read and infered\n            \"{},otel::tracing=trace,otel=debug\",\n            std::env::var(\"RUST_LOG\")\n                .or_else(|_| std::env::var(\"OTEL_LOG_LEVEL\"))\n                .unwrap_or_else(|_| \"info\".to_string())\n        ),\n    );\n    EnvFilter::from_default_env()\n}\n\npub fn build_otel_layer<S>() -> Result<OpenTelemetryLayer<S, Tracer>, BoxError>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n{\n    use crate::{\n        init_propagator, //stdio,\n        otlp,\n        resource::DetectResource,\n    };\n    let otel_rsrc = DetectResource::default()\n        //.with_fallback_service_name(env!(\"CARGO_PKG_NAME\"))\n        //.with_fallback_service_version(env!(\"CARGO_PKG_VERSION\"))\n        .build();\n    let otel_tracer = otlp::init_tracer(otel_rsrc, otlp::identity)?;\n    // to not send trace somewhere, but continue to create and propagate,...\n    // then send them to `axum_tracing_opentelemetry::stdio::WriteNoWhere::default()`\n    // or to `std::io::stdout()` to print\n    //\n    // let otel_tracer =\n    //     stdio::init_tracer(otel_rsrc, stdio::identity, stdio::WriteNoWhere::default())?;\n    init_propagator()?;\n    Ok(tracing_opentelemetry::layer().with_tracer(otel_tracer))\n}\n```\n\nTo retrieve the current `trace_id` (eg to add it into error message (as header or attributes))\n\n```rust\n  # use tracing_opentelemetry_instrumentation_sdk;\n\n  let trace_id = tracing_opentelemetry_instrumentation_sdk::find_current_trace_id();\n  //json!({ \"error\" :  \"xxxxxx\", \"trace_id\": trace_id})\n```\n\n## Configuration based on the environment variables\n\nTo ease setup and compliance with [OpenTelemetry SDK configuration](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/), the configuration can be done with the following environment variables (see sample `init_tracing()` above):\n\n- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` fallback to `OTEL_EXPORTER_OTLP_ENDPOINT` for the url of the exporter / collector\n- `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` fallback to `OTEL_EXPORTER_OTLP_PROTOCOL`, fallback to auto-detection based on ENDPOINT port\n- `OTEL_SERVICE_NAME` for the name of the service\n- `OTEL_PROPAGATORS` for the configuration of the propagators\n- `OTEL_TRACES_SAMPLER` & `OTEL_TRACES_SAMPLER_ARG` for configuration of the sampler\n\nFew other environment variables can also be used to configure OTLP exporter (eg to configure headers, authentication,, etc...):\n\n- [`OTEL_EXPORTER_OTLP_HEADERS`](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_headers)\n- [`OTEL_EXPORTER_OTLP_TRACES_HEADERS`](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_traces_headers)\n\n```sh\n# For GRPC:\nexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://localhost:4317\"\nexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=\"grpc\"\nexport OTEL_TRACES_SAMPLER=\"always_on\"\n\n# For HTTP:\nexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=\"http://127.0.0.1:4318/v1/traces\"\nexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=\"http/protobuf\"\nexport OTEL_TRACES_SAMPLER=\"always_on\"\n```\n\nIn the context of **kubernetes**, some of the above environment variables can be injected by the Opentelemetry operator (via `inject-sdk`):\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nspec:\n  template:\n    metadata:\n      annotations:\n        # to inject environment variables only by opentelemetry-operator\n        instrumentation.opentelemetry.io/inject-sdk: \"opentelemetry-operator/instrumentation\"\n        instrumentation.opentelemetry.io/container-names: \"app\"\n      containers:\n        - name: app\n```\n\nOr if you don't setup `inject-sdk`, you can manually set the environment variable eg\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nspec:\n  template:\n    metadata:\n      containers:\n        - name: app\n          env:\n            - name: OTEL_SERVICE_NAME\n              value: \"app\"\n            - name: OTEL_EXPORTER_OTLP_PROTOCOL\n              value: \"grpc\"\n            # for otel collector in `deployment` mode, use the name of the service\n            # - name: OTEL_EXPORTER_OTLP_ENDPOINT\n            #   value: \"http://opentelemetry-collector.opentelemetry-collector:4317\"\n            # for otel collector in sidecar mode (imply to deploy a sidecar CR per namespace)\n            - name: OTEL_EXPORTER_OTLP_ENDPOINT\n              value: \"http://localhost:4317\"\n            # for `daemonset` mode: need to use the local daemonset (value interpolated by k8s: `$(...)`)\n            # - name: OTEL_EXPORTER_OTLP_ENDPOINT\n            #   value: \"http://$(HOST_IP):4317\"\n            # - name: HOST_IP\n            #   valueFrom:\n            #     fieldRef:\n            #       fieldPath: status.hostIP\n```\n\n## Troubleshot why no trace?\n\n- check you only have a single version of opentelemtry (could be part of your CI/build), use `cargo-deny` or `cargo tree`\n\n  ```sh\n  # Check only one version of opentelemetry should be used\n  # else issue with setup of global (static variable)\n  # check_single_version_opentelemtry:\n  cargo tree -i opentelemetry\n  ```\n\n- check the code of your exporter and the integration with `tracing` (as subscriber's layer)\n- check the environment variables of opentelemetry `OTEL_EXPORTER...` and `OTEL_TRACES_SAMPLER` (values are logged on target `otel::setup` )\n- check that log target `otel::tracing` enable log level `trace` (or `info` if you use `tracing_level_info` feature) to generate span to send to opentelemetry collector.\n\n## Metrics\n\nTo configure opentelemetry metrics, enable the `metrics` feature, this will initialize a `SdkMeterProvider`, set it globally and add a a [`MetricsLayer`](https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/struct.MetricsLayer.html) to allow using `tracing` events to produce metrics.\n\nThe `opentelemetry_sdk` can still be used to produce metrics as well, since we configured the `SdkMeterProvider` globally, so any Axum/Tonic middleware that does not use `tracing` but directly [opentelemetry::metrics](https://docs.rs/opentelemetry/latest/opentelemetry/metrics/struct.Meter.html) will work.\n\nConfigure the following set of environment variables to configure the metrics exporter (on top of those configured above):\n\n- `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` override to `OTEL_EXPORTER_OTLP_ENDPOINT` for the url of the exporter / collector\n- `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` override to `OTEL_EXPORTER_OTLP_PROTOCOL`, fallback to auto-detection based on ENDPOINT port\n- `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` to set the timeout for the connection to the exporter\n- `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` to set the temporality preference for the exporter\n- `OTEL_METRIC_EXPORT_INTERVAL` to set frequence of metrics export in **_milliseconds_**, defaults to 60s\n\n## Logs\n\nTo configure OpenTelemetry log export, enable the `logs` feature. This initializes a `SdkLoggerProvider` and adds a log bridge layer so that `tracing` events are forwarded to the OpenTelemetry log pipeline and exported via OTLP.\n\n```toml\n[dependencies]\ninit-tracing-opentelemetry = { version = \"*\", features = [\"otlp\", \"logs\"] }\n```\n\nStandard `tracing` macros emit logs that are automatically bridged:\n\n```rust\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let _guard = init_tracing_opentelemetry::TracingConfig::production().init_subscriber()?;\n\n    tracing::error!(\"This is ground control to Major Tom\");\n    tracing::warn!(\"Houston, we have a problem\");\n    tracing::info!(\"We have contact\");\n    tracing::debug!(\"Roger, copy that\");\n\n    Ok(())\n}\n```\n\nLog export can be toggled at runtime via `.with_logs(bool)`:\n\n```rust,no_run\nuse init_tracing_opentelemetry::TracingConfig;\n//...\nTracingConfig::default()\n    .with_logs(false)   // disable log export (default: enabled when feature is active)\n    .init_subscriber()\n    .expect(\"valid tracing configuration\");\n```\n> Traces are automatically attached to logs as well, so if the logs are queried in Grafana (for example), the trace automatically links to the log line.\n\n![screenshot of grafana logs](https://raw.githubusercontent.com/davidB/tracing-opentelemetry-instrumentation-sdk/refs/heads/main/examples/logging/screenshot_grafana.png)\n\nConfigure the following environment variables to control the logs exporter (in addition to the shared variables above):\n\n- `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for the log pipeline; for HTTP the path `/v1/logs` is appended automatically\n- `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` overrides `OTEL_EXPORTER_OTLP_PROTOCOL`, falls back to port-based auto-detection\n\n```sh\n# For GRPC:\nexport OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=\"http://localhost:4317\"\nexport OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=\"grpc\"\n\n# For HTTP:\nexport OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=\"http://127.0.0.1:4318/v1/logs\"\nexport OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=\"http/protobuf\"\n```\n\n> **Note:** A protocol must be set (via env var or inferable from the endpoint port). If neither is found, no log exporter is created and a warning is emitted on target `otel::setup`.\n\n## Changelog - History\n\n[CHANGELOG.md](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/main/CHANGELOG.md)\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/config.rs",
    "content": "//! Flexible tracing configuration with builder pattern.\n//!\n//! Provides [`TracingConfig`] for configurable tracing setup with format options,\n//! output destinations, level filtering, and OpenTelemetry integration.\n//!\n//! # Example\n//! ```no_run\n//! use init_tracing_opentelemetry::TracingConfig;\n//!\n//! // Use preset with global subscriber (default)\n//! let _guard = TracingConfig::development().init_subscriber()?;\n//!\n//! // Custom configuration with global subscriber\n//! let _guard = TracingConfig::default()\n//!     .with_json_format()\n//!     .with_stderr()\n//!     .with_log_directives(\"debug\")\n//!     .init_subscriber()?;\n//!\n//! // Non-global subscriber (thread-local)\n//! let guard = TracingConfig::development()\n//!     .with_global_subscriber(false)\n//!     .init_subscriber()?;\n//! // Guard must be kept alive for subscriber to remain active\n//! assert!(guard.is_non_global());\n//!\n//! // Without OpenTelemetry (just logging)\n//! let guard = TracingConfig::minimal()\n//!     .with_otel(false)\n//!     .init_subscriber()?;\n//! // Works fine - guard.otel_guard is None\n//! assert!(!guard.has_otel());\n//! assert!(guard.otel_guard.is_none());\n//!\n//! // Direct field access is also possible\n//! if let Some(otel_guard) = &guard.otel_guard {\n//!     // Use otel_guard...\n//! }\n//! # Ok::<(), Box<dyn std::error::Error>>(())\n//! ```\n#![allow(deprecated)]\n\nuse std::path::{Path, PathBuf};\n\nuse tracing::{Subscriber, info, level_filters::LevelFilter};\nuse tracing_subscriber::{\n    Layer, Registry, filter::EnvFilter, fmt::format::FmtSpan, layer::SubscriberExt,\n    registry::LookupSpan,\n};\n\n#[cfg(feature = \"logfmt\")]\nuse crate::formats::LogfmtLayerBuilder;\nuse crate::formats::{\n    CompactLayerBuilder, FullLayerBuilder, JsonLayerBuilder, LayerBuilder, PrettyLayerBuilder,\n};\n\n#[cfg(feature = \"logs\")]\nuse crate::tracing_subscriber_ext::build_logger_layer_with_resource;\n#[cfg(feature = \"metrics\")]\nuse crate::tracing_subscriber_ext::build_metrics_layer_with_resource;\nuse crate::tracing_subscriber_ext::build_tracer_layer_with_resource_and_name;\nuse crate::{Error, otlp::OtelGuard, resource::DetectResource};\n\n/// Combined guard that handles both `OtelGuard` and optional `DefaultGuard`\n///\n/// This struct holds the various guards needed to maintain the tracing subscriber.\n/// - `otel_guard`: OpenTelemetry guard for flushing traces/metrics on drop (None when OTEL disabled)\n/// - `default_guard`: Subscriber default guard for non-global subscribers (None when using global)\n#[must_use = \"Recommend holding with 'let _guard = ' pattern to ensure final traces/log/metrics are sent to the server and subscriber is maintained\"]\npub struct Guard {\n    /// OpenTelemetry guard for proper cleanup (None when OTEL is disabled)\n    pub otel_guard: Option<OtelGuard>,\n    /// Default subscriber guard for non-global mode (None when using global subscriber)\n    pub default_guard: Option<tracing::subscriber::DefaultGuard>,\n    // Easy to add in the future:\n    // pub log_guard: Option<LogGuard>,\n    // pub metrics_guard: Option<MetricsGuard>,\n}\n\nimpl Guard {\n    /// Create a new Guard for global subscriber mode\n    pub fn global(otel_guard: Option<OtelGuard>) -> Self {\n        Self {\n            otel_guard,\n            default_guard: None,\n        }\n    }\n\n    /// Create a new Guard for non-global subscriber mode\n    pub fn non_global(\n        otel_guard: Option<OtelGuard>,\n        default_guard: tracing::subscriber::DefaultGuard,\n    ) -> Self {\n        Self {\n            otel_guard,\n            default_guard: Some(default_guard),\n        }\n    }\n\n    /// Get a reference to the underlying `OtelGuard` if present\n    #[must_use]\n    pub fn otel_guard(&self) -> Option<&OtelGuard> {\n        self.otel_guard.as_ref()\n    }\n\n    /// Check if OpenTelemetry is enabled for this guard\n    #[must_use]\n    pub fn has_otel(&self) -> bool {\n        self.otel_guard.is_some()\n    }\n\n    /// Check if this guard is managing a non-global (thread-local) subscriber\n    #[must_use]\n    pub fn is_non_global(&self) -> bool {\n        self.default_guard.is_some()\n    }\n\n    /// Check if this guard is for a global subscriber\n    #[must_use]\n    pub fn is_global(&self) -> bool {\n        self.default_guard.is_none()\n    }\n}\n\n/// Configuration for log output format\n#[derive(Debug, Clone)]\npub enum LogFormat {\n    /// Pretty formatted output with colors and indentation (development)\n    Pretty,\n    /// Structured JSON output (production)\n    Json,\n    /// Single-line output\n    Full,\n    /// Single-line compact output\n    Compact,\n    /// Key=value logfmt format\n    #[cfg(feature = \"logfmt\")]\n    Logfmt,\n}\n\nimpl Default for LogFormat {\n    fn default() -> Self {\n        if cfg!(debug_assertions) {\n            LogFormat::Pretty\n        } else {\n            LogFormat::Json\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum LogTimer {\n    None,\n    Time,\n    Uptime,\n}\n\nimpl Default for LogTimer {\n    fn default() -> Self {\n        if cfg!(debug_assertions) {\n            LogTimer::Uptime\n        } else {\n            LogTimer::Time\n        }\n    }\n}\n\n/// Configuration for log output destination\n#[derive(Debug, Clone, Default)]\npub enum WriterConfig {\n    /// Write to stdout\n    #[default]\n    Stdout,\n    /// Write to stderr\n    Stderr,\n    /// Write to a file\n    File(PathBuf),\n}\n\n/// Configuration for log level filtering\n#[derive(Debug, Clone)]\npub struct LevelConfig {\n    /// Log directives string (takes precedence over env vars)\n    pub directives: String,\n    /// Environment variable fallbacks (checked in order)\n    pub env_fallbacks: Vec<String>,\n    /// Default level when no directives or env vars are set\n    pub default_level: LevelFilter,\n    /// OpenTelemetry tracing level\n    pub otel_trace_level: LevelFilter,\n}\n\nimpl Default for LevelConfig {\n    fn default() -> Self {\n        Self {\n            directives: String::new(),\n            env_fallbacks: vec![\"RUST_LOG\".to_string(), \"OTEL_LOG_LEVEL\".to_string()],\n            default_level: LevelFilter::INFO,\n            otel_trace_level: LevelFilter::TRACE,\n        }\n    }\n}\n\n/// Configuration for optional tracing features\n#[derive(Debug, Clone)]\n#[allow(clippy::struct_excessive_bools)]\npub struct FeatureSet {\n    /// Include file names in output\n    pub file_names: bool,\n    /// Include line numbers in output\n    pub line_numbers: bool,\n    /// Include thread names in output\n    pub thread_names: bool,\n    /// Include thread IDs in output\n    pub thread_ids: bool,\n    /// Configure time logging (wall clock, uptime or none)\n    pub timer: LogTimer,\n    /// Configure span event logging\n    pub span_events: Option<FmtSpan>,\n    /// Display target information\n    pub target_display: bool,\n}\n\nimpl Default for FeatureSet {\n    fn default() -> Self {\n        Self {\n            file_names: true,\n            line_numbers: cfg!(debug_assertions),\n            thread_names: cfg!(debug_assertions),\n            thread_ids: false,\n            timer: LogTimer::default(),\n            span_events: if cfg!(debug_assertions) {\n                Some(FmtSpan::NEW | FmtSpan::CLOSE)\n            } else {\n                None\n            },\n            target_display: true,\n        }\n    }\n}\n\n/// Configuration for OpenTelemetry integration\n#[derive(Debug)]\npub struct OtelConfig {\n    /// Enable OpenTelemetry tracing\n    pub enabled: bool,\n    /// Resource configuration for OTEL\n    pub resource_config: Option<DetectResource>,\n    /// Enable log export via OTLP\n    pub logs_enabled: bool,\n    /// Enable metrics collection\n    pub metrics_enabled: bool,\n}\n\nimpl Default for OtelConfig {\n    fn default() -> Self {\n        Self {\n            enabled: true,\n            resource_config: None,\n            logs_enabled: cfg!(feature = \"logs\"),\n            metrics_enabled: cfg!(feature = \"metrics\"),\n        }\n    }\n}\n\n/// Main configuration builder for tracing setup\n/// Default create a new tracing configuration with sensible defaults\n#[derive(Debug)]\npub struct TracingConfig {\n    /// Output format configuration\n    pub format: LogFormat,\n    /// Output destination configuration\n    pub writer: WriterConfig,\n    /// Level filtering configuration\n    pub level_config: LevelConfig,\n    /// Optional features configuration\n    pub features: FeatureSet,\n    /// OpenTelemetry configuration\n    pub otel_config: OtelConfig,\n    /// Whether to set the subscriber as global default\n    pub global_subscriber: bool,\n    /// name of the tracer (default `\"\"`)\n    pub tracer_name: String,\n}\n\nimpl Default for TracingConfig {\n    fn default() -> Self {\n        Self {\n            format: LogFormat::default(),\n            writer: WriterConfig::default(),\n            level_config: LevelConfig::default(),\n            features: FeatureSet::default(),\n            otel_config: OtelConfig::default(),\n            global_subscriber: true,\n            tracer_name: String::new(),\n        }\n    }\n}\n\nimpl TracingConfig {\n    // === Format Configuration ===\n\n    /// Set the log format\n    #[must_use]\n    pub fn with_format(mut self, format: LogFormat) -> Self {\n        self.format = format;\n        self\n    }\n\n    /// Use pretty formatted output (development style)\n    #[must_use]\n    pub fn with_pretty_format(self) -> Self {\n        self.with_format(LogFormat::Pretty)\n    }\n\n    /// Use JSON formatted output (production style)\n    #[must_use]\n    pub fn with_json_format(self) -> Self {\n        self.with_format(LogFormat::Json)\n    }\n\n    /// Use full formatted output\n    #[must_use]\n    pub fn with_full_format(self) -> Self {\n        self.with_format(LogFormat::Full)\n    }\n\n    /// Use compact formatted output\n    #[must_use]\n    pub fn with_compact_format(self) -> Self {\n        self.with_format(LogFormat::Compact)\n    }\n\n    /// Use logfmt formatted output (requires 'logfmt' feature)\n    #[must_use]\n    #[cfg(feature = \"logfmt\")]\n    pub fn with_logfmt_format(self) -> Self {\n        self.with_format(LogFormat::Logfmt)\n    }\n\n    // === Writer Configuration ===\n\n    /// Set the output writer\n    #[must_use]\n    pub fn with_writer(mut self, writer: WriterConfig) -> Self {\n        self.writer = writer;\n        self\n    }\n\n    /// Write logs to stdout\n    #[must_use]\n    pub fn with_stdout(self) -> Self {\n        self.with_writer(WriterConfig::Stdout)\n    }\n\n    /// Write logs to stderr\n    #[must_use]\n    pub fn with_stderr(self) -> Self {\n        self.with_writer(WriterConfig::Stderr)\n    }\n\n    /// Write logs to a file\n    #[must_use]\n    pub fn with_file<P: AsRef<Path>>(self, path: P) -> Self {\n        self.with_writer(WriterConfig::File(path.as_ref().to_path_buf()))\n    }\n\n    // === Level Configuration ===\n\n    /// Set log directives (takes precedence over environment variables),\n    /// for example if you want to set it from cli arguments (verbosity)\n    #[must_use]\n    pub fn with_log_directives(mut self, directives: impl Into<String>) -> Self {\n        self.level_config.directives = directives.into();\n        self\n    }\n\n    /// Set the default log level\n    #[must_use]\n    pub fn with_default_level(mut self, level: LevelFilter) -> Self {\n        self.level_config.default_level = level;\n        self\n    }\n\n    /// Add an environment variable fallback for log configuration\n    #[must_use]\n    pub fn with_env_fallback(mut self, env_var: impl Into<String>) -> Self {\n        self.level_config.env_fallbacks.push(env_var.into());\n        self\n    }\n\n    /// Set the OpenTelemetry trace level\n    #[must_use]\n    pub fn with_otel_trace_level(mut self, level: LevelFilter) -> Self {\n        self.level_config.otel_trace_level = level;\n        self\n    }\n\n    /// Set the name of the tracer (default `\"\"`)\n    #[must_use]\n    pub fn with_otel_tracer_name(mut self, name: impl Into<String>) -> Self {\n        self.tracer_name = name.into();\n        self\n    }\n\n    // === Feature Configuration ===\n\n    /// Enable or disable file names in output\n    #[must_use]\n    pub fn with_file_names(mut self, enabled: bool) -> Self {\n        self.features.file_names = enabled;\n        self\n    }\n\n    /// Enable or disable line numbers in output\n    #[must_use]\n    pub fn with_line_numbers(mut self, enabled: bool) -> Self {\n        self.features.line_numbers = enabled;\n        self\n    }\n\n    /// Enable or disable thread names in output\n    #[must_use]\n    pub fn with_thread_names(mut self, enabled: bool) -> Self {\n        self.features.thread_names = enabled;\n        self\n    }\n\n    /// Enable or disable thread IDs in output\n    #[must_use]\n    pub fn with_thread_ids(mut self, enabled: bool) -> Self {\n        self.features.thread_ids = enabled;\n        self\n    }\n\n    /// Configure span event logging\n    #[must_use]\n    pub fn with_span_events(mut self, events: FmtSpan) -> Self {\n        self.features.span_events = Some(events);\n        self\n    }\n\n    /// Disable span event logging\n    #[must_use]\n    pub fn without_span_events(mut self) -> Self {\n        self.features.span_events = None;\n        self\n    }\n\n    /// Enable or disable uptime timer (vs wall clock)\n    #[must_use]\n    #[deprecated = \"Use `TracingConfig::with_timer` instead\"]\n    pub fn with_uptime_timer(mut self, enabled: bool) -> Self {\n        self.features.timer = if enabled {\n            LogTimer::Uptime\n        } else {\n            LogTimer::Time\n        };\n        self\n    }\n\n    /// Configure time logging (wall clock, uptime or none)\n    #[must_use]\n    pub fn with_timer(mut self, timer: LogTimer) -> Self {\n        self.features.timer = timer;\n        self\n    }\n\n    /// Enable or disable target display\n    #[must_use]\n    pub fn with_target_display(mut self, enabled: bool) -> Self {\n        self.features.target_display = enabled;\n        self\n    }\n\n    // === OpenTelemetry Configuration ===\n\n    /// Enable or disable OpenTelemetry tracing\n    #[must_use]\n    pub fn with_otel(mut self, enabled: bool) -> Self {\n        self.otel_config.enabled = enabled;\n        self\n    }\n\n    /// Enable or disable log export via OTLP\n    #[must_use]\n    pub fn with_logs(mut self, enabled: bool) -> Self {\n        self.otel_config.logs_enabled = enabled;\n        self\n    }\n\n    /// Enable or disable metrics collection\n    #[must_use]\n    pub fn with_metrics(mut self, enabled: bool) -> Self {\n        self.otel_config.metrics_enabled = enabled;\n        self\n    }\n\n    /// Set resource configuration for OpenTelemetry\n    #[must_use]\n    pub fn with_resource_config(mut self, config: DetectResource) -> Self {\n        self.otel_config.resource_config = Some(config);\n        self\n    }\n\n    /// Set whether to initialize the subscriber as global default\n    ///\n    /// When `global` is true (default), the subscriber is set as the global default.\n    /// When false, the subscriber is set as thread-local default and the returned\n    /// Guard must be kept alive to maintain the subscriber.\n    #[must_use]\n    pub fn with_global_subscriber(mut self, global: bool) -> Self {\n        self.global_subscriber = global;\n        self\n    }\n\n    // === Build Methods ===\n\n    /// Build a tracing layer with the current configuration\n    pub fn build_layer<S>(&self) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        match &self.format {\n            LogFormat::Pretty => PrettyLayerBuilder.build_layer(self),\n            LogFormat::Json => JsonLayerBuilder.build_layer(self),\n            LogFormat::Full => FullLayerBuilder.build_layer(self),\n            LogFormat::Compact => CompactLayerBuilder.build_layer(self),\n            #[cfg(feature = \"logfmt\")]\n            LogFormat::Logfmt => LogfmtLayerBuilder.build_layer(self),\n        }\n    }\n\n    /// Build a level filter layer with the current configuration\n    pub fn build_filter_layer(&self) -> Result<EnvFilter, Error> {\n        // Use existing function but with our configuration\n        let dirs = if self.level_config.directives.is_empty() {\n            // Try environment variables in order\n            self.level_config\n                .env_fallbacks\n                .iter()\n                .find_map(|var| std::env::var(var).ok())\n                .unwrap_or_else(|| self.level_config.default_level.to_string().to_lowercase())\n        } else {\n            self.level_config.directives.clone()\n        };\n\n        let directive_to_allow_otel_trace = format!(\n            \"otel::tracing={}\",\n            self.level_config\n                .otel_trace_level\n                .to_string()\n                .to_lowercase()\n        )\n        .parse()?;\n\n        Ok(EnvFilter::builder()\n            .with_default_directive(self.level_config.default_level.into())\n            .parse_lossy(dirs)\n            .add_directive(directive_to_allow_otel_trace))\n    }\n\n    /// Initialize the tracing subscriber with this configuration\n    ///\n    /// If `global_subscriber` is true, sets the subscriber as the global default.\n    /// If false, returns a Guard that maintains the subscriber as the thread-local default.\n    ///\n    /// When OpenTelemetry is disabled, the Guard will contain `None` for the `OtelGuard`.\n    pub fn init_subscriber(self) -> Result<Guard, Error> {\n        self.init_subscriber_ext(Self::transform_identity)\n    }\n\n    fn transform_identity(s: Registry) -> Registry {\n        s\n    }\n\n    /// `transform` parameter allow to customize the registry/subscriber before\n    /// the setup of opentelemetry, log, logfilter.\n    /// ```text\n    /// let guard = TracingConfig::default()\n    ///    .with_json_format()\n    ///    .with_stderr()\n    ///    .init_subscriber_ext(|subscriber| subscriber.with(my_layer))?;\n    /// ```\n    pub fn init_subscriber_ext<F, SOut>(self, transform: F) -> Result<Guard, Error>\n    where\n        SOut: Subscriber + for<'a> LookupSpan<'a> + Send + Sync,\n        F: FnOnce(Registry) -> SOut,\n    {\n        // Setup a temporary subscriber for initialization logging\n        let temp_subscriber = tracing_subscriber::registry()\n            .with(self.build_layer()?)\n            .with(self.build_filter_layer()?);\n        let _guard = tracing::subscriber::set_default(temp_subscriber);\n        info!(\"init logging & tracing\");\n\n        // Build the final subscriber based on OTEL configuration\n        if self.otel_config.enabled {\n            let subscriber = transform(tracing_subscriber::registry());\n            let layer = self.build_layer()?;\n            let filter_layer = self.build_filter_layer()?;\n            let (subscriber, otel_guard) = self.register_otel_layers_with_resource(subscriber)?;\n            let subscriber = subscriber.with(layer).with(filter_layer);\n\n            if self.global_subscriber {\n                tracing::subscriber::set_global_default(subscriber)?;\n                Ok(Guard::global(Some(otel_guard)))\n            } else {\n                let default_guard = tracing::subscriber::set_default(subscriber);\n                Ok(Guard::non_global(Some(otel_guard), default_guard))\n            }\n        } else {\n            info!(\"OpenTelemetry disabled - proceeding without OTEL layers\");\n            let subscriber = transform(tracing_subscriber::registry())\n                .with(self.build_layer()?)\n                .with(self.build_filter_layer()?);\n\n            if self.global_subscriber {\n                tracing::subscriber::set_global_default(subscriber)?;\n                Ok(Guard::global(None))\n            } else {\n                let default_guard = tracing::subscriber::set_default(subscriber);\n                Ok(Guard::non_global(None, default_guard))\n            }\n        }\n    }\n\n    fn register_otel_layers_with_resource<S>(\n        &self,\n        subscriber: S,\n    ) -> Result<(impl Subscriber + for<'span> LookupSpan<'span>, OtelGuard), Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        let otel_rsrc = self\n            .otel_config\n            .resource_config\n            .clone()\n            .unwrap_or_default()\n            .build();\n        #[cfg(feature = \"logs\")]\n        let (logs_layer, logger_provider) = build_logger_layer_with_resource(otel_rsrc.clone())?;\n        #[cfg(feature = \"metrics\")]\n        let (metrics_layer, meter_provider) = build_metrics_layer_with_resource(otel_rsrc.clone())?;\n        let (trace_layer, tracer_provider) =\n            build_tracer_layer_with_resource_and_name(otel_rsrc, self.tracer_name.clone())?;\n        let subscriber = subscriber.with(trace_layer);\n        #[cfg(feature = \"logs\")]\n        let subscriber = subscriber.with(self.otel_config.logs_enabled.then_some(logs_layer));\n        #[cfg(feature = \"metrics\")]\n        let subscriber = subscriber.with(self.otel_config.metrics_enabled.then_some(metrics_layer));\n        Ok((\n            subscriber,\n            OtelGuard {\n                #[cfg(feature = \"logs\")]\n                logger_provider,\n                #[cfg(feature = \"metrics\")]\n                meter_provider,\n                tracer_provider,\n            },\n        ))\n    }\n\n    // === Preset Configurations ===\n\n    /// Configuration preset for development environments\n    /// - Pretty formatting with colors\n    /// - Output to stderr\n    /// - Line numbers and thread names enabled\n    /// - Span events for NEW and CLOSE\n    /// - Full OpenTelemetry integration\n    #[must_use]\n    pub fn development() -> Self {\n        Self::default()\n            .with_pretty_format()\n            .with_stderr()\n            .with_line_numbers(true)\n            .with_thread_names(true)\n            .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)\n            .with_otel(true)\n    }\n\n    /// Configuration preset for production environments\n    /// - JSON formatting for structured logging\n    /// - Output to stdout\n    /// - Minimal metadata (no line numbers or thread names)\n    /// - No span events to reduce verbosity\n    /// - Full OpenTelemetry integration\n    #[must_use]\n    pub fn production() -> Self {\n        Self::default()\n            .with_json_format()\n            .with_stdout()\n            .with_line_numbers(false)\n            .with_thread_names(false)\n            .without_span_events()\n            .with_otel(true)\n    }\n\n    /// Configuration preset for debugging\n    /// - Pretty formatting with full verbosity\n    /// - Output to stderr\n    /// - All metadata enabled\n    /// - Full span events\n    /// - Debug level logging\n    /// - Full OpenTelemetry integration\n    #[must_use]\n    pub fn debug() -> Self {\n        Self::development()\n            .with_log_directives(\"debug\")\n            .with_span_events(FmtSpan::FULL)\n            .with_target_display(true)\n    }\n\n    /// Configuration preset for minimal logging\n    /// - Compact formatting\n    /// - Output to stdout\n    /// - No metadata or extra features\n    /// - OpenTelemetry disabled for minimal overhead\n    #[must_use]\n    pub fn minimal() -> Self {\n        Self::default()\n            .with_compact_format()\n            .with_stdout()\n            .with_line_numbers(false)\n            .with_thread_names(false)\n            .without_span_events()\n            .with_target_display(false)\n            .with_otel(false)\n    }\n\n    /// Configuration preset for testing environments\n    /// - Compact formatting for less noise\n    /// - Output to stderr to separate from test output\n    /// - Basic metadata\n    /// - OpenTelemetry disabled for speed\n    /// - non global registration (of subscriber)\n    #[must_use]\n    pub fn testing() -> Self {\n        Self::default()\n            .with_compact_format()\n            .with_stderr()\n            .with_line_numbers(false)\n            .with_thread_names(false)\n            .without_span_events()\n            .with_otel(false)\n            .with_global_subscriber(false)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_global_subscriber_true_returns_global_guard() {\n        let config = TracingConfig::minimal()\n            .with_global_subscriber(true)\n            .with_otel(false); // Disable for simple test\n\n        // This would actually initialize the subscriber, so we'll just test that\n        // the config has the right value\n        assert!(config.global_subscriber);\n    }\n\n    #[test]\n    fn test_global_subscriber_false_sets_config() {\n        let config = TracingConfig::minimal()\n            .with_global_subscriber(false)\n            .with_otel(false); // Disable for simple test\n\n        assert!(!config.global_subscriber);\n    }\n\n    #[test]\n    fn test_default_global_subscriber_is_true() {\n        let config = TracingConfig::default();\n        assert!(config.global_subscriber);\n    }\n\n    #[test]\n    fn test_init_subscriber_without_otel_succeeds() {\n        // Test that initialization succeeds when OTEL is disabled\n        let guard = TracingConfig::minimal()\n            .with_otel(false)\n            .with_global_subscriber(false) // Use non-global to avoid affecting other tests\n            .init_subscriber();\n\n        assert!(guard.is_ok());\n        let guard = guard.unwrap();\n\n        // Verify that the guard indicates no OTEL\n        assert!(!guard.has_otel());\n        assert!(guard.otel_guard().is_none());\n    }\n\n    #[test]\n    fn test_init_subscriber_with_otel_disabled_global() {\n        // Test global subscriber mode with OTEL disabled\n        let guard = TracingConfig::minimal()\n            .with_otel(false)\n            .with_global_subscriber(true)\n            .init_subscriber();\n\n        assert!(guard.is_ok());\n        let guard = guard.unwrap();\n\n        // Should be global mode with no OTEL\n        assert!(guard.is_global());\n        assert!(!guard.has_otel());\n        assert!(guard.otel_guard().is_none());\n    }\n\n    #[test]\n    fn test_init_subscriber_with_otel_disabled_non_global() {\n        // Test non-global subscriber mode with OTEL disabled\n        let guard = TracingConfig::minimal()\n            .with_otel(false)\n            .with_global_subscriber(false)\n            .init_subscriber();\n\n        assert!(guard.is_ok());\n        let guard = guard.unwrap();\n\n        // Should be non-global mode with no OTEL\n        assert!(guard.is_non_global());\n        assert!(!guard.has_otel());\n        assert!(guard.otel_guard().is_none());\n    }\n\n    #[test]\n    fn test_guard_helper_methods() {\n        // Test the Guard helper methods work correctly with None values\n        let guard_global_none = Guard::global(None);\n        assert!(!guard_global_none.has_otel());\n        assert!(guard_global_none.otel_guard().is_none());\n        assert!(guard_global_none.is_global());\n        assert!(!guard_global_none.is_non_global());\n        assert!(guard_global_none.default_guard.is_none());\n\n        // We can't easily create a DefaultGuard for testing, but we can test the constructor\n        // Note: We can't actually create a DefaultGuard without setting up a real subscriber,\n        // so we'll just test the struct design is sound\n    }\n\n    #[test]\n    fn test_guard_struct_direct_field_access() {\n        // Test that we can directly access fields, which is a benefit of the struct design\n        let guard = Guard::global(None);\n\n        // Direct field access is now possible\n        assert!(guard.otel_guard.is_none());\n        assert!(guard.default_guard.is_none());\n\n        // Helper methods still work\n        assert!(!guard.has_otel());\n        assert!(guard.is_global());\n    }\n\n    #[test]\n    fn test_guard_struct_extensibility() {\n        // This test demonstrates how the struct design makes it easier to extend\n        // We can easily add more optional guards in the future without breaking existing code\n        let guard = Guard {\n            otel_guard: None,\n            default_guard: None,\n            // Future: log_guard: None, metrics_guard: None, etc.\n        };\n\n        assert!(guard.is_global());\n        assert!(!guard.has_otel());\n    }\n\n    #[tokio::test]\n    async fn test_init_with_transform() {\n        use std::time::Duration;\n        use tokio_blocked::TokioBlockedLayer;\n        let blocked =\n            TokioBlockedLayer::new().with_warn_busy_single_poll(Some(Duration::from_micros(150)));\n\n        let guard = TracingConfig::default()\n            .with_json_format()\n            .with_stderr()\n            .with_log_directives(\"debug\")\n            .with_global_subscriber(false)\n            .init_subscriber_ext(|subscriber| subscriber.with(blocked))\n            .unwrap();\n\n        assert!(!guard.is_global());\n        assert!(guard.has_otel());\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/error.rs",
    "content": "#[derive(thiserror::Error, Debug)]\npub enum Error {\n    #[error(transparent)]\n    IoError(#[from] std::io::Error),\n\n    #[error(transparent)]\n    SetGlobalDefaultError(#[from] tracing::subscriber::SetGlobalDefaultError),\n\n    #[error(transparent)]\n    TraceError(#[from] opentelemetry_sdk::trace::TraceError),\n\n    #[cfg(feature = \"otlp\")]\n    #[error(transparent)]\n    ExporterBuildError(#[from] opentelemetry_otlp::ExporterBuildError),\n\n    #[cfg(feature = \"tracing_subscriber_ext\")]\n    #[error(transparent)]\n    FilterParseError(#[from] tracing_subscriber::filter::ParseError),\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/formats.rs",
    "content": "//! Format-specific layer builders for tracing output.\n//!\n//! Provides implementations for different log formats (Pretty, JSON, Compact, Logfmt)\n//! using the strategy pattern with the [`LayerBuilder`] trait.\n\nuse tracing::Subscriber;\nuse tracing_subscriber::fmt;\nuse tracing_subscriber::fmt::format::FmtSpan;\nuse tracing_subscriber::fmt::time::{Uptime, time, uptime};\nuse tracing_subscriber::{Layer, registry::LookupSpan};\n\nuse crate::config::{LogTimer, TracingConfig, WriterConfig};\nuse crate::{Error, FeatureSet};\n\n/// Trait for building format-specific tracing layers\npub trait LayerBuilder: Send + Sync {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>;\n}\n\nfn configure_layer<S, N, L, T, W>(\n    mut layer: fmt::Layer<S, N, fmt::format::Format<L, T>, W>,\n    config: &TracingConfig,\n) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n    N: for<'writer> fmt::FormatFields<'writer> + Send + Sync + 'static,\n    L: Send + Sync + 'static,\n    fmt::format::Format<L, ()>: fmt::FormatEvent<S, N>,\n    fmt::format::Format<L, Uptime>: fmt::FormatEvent<S, N>,\n    fmt::format::Format<L>: fmt::FormatEvent<S, N>,\n    W: for<'writer> fmt::MakeWriter<'writer> + Send + Sync + 'static,\n{\n    // NOTE: Destructure to make sure we don’t miss a feature\n    let FeatureSet {\n        file_names,\n        line_numbers,\n        thread_names,\n        thread_ids,\n        timer,\n        span_events,\n        target_display,\n    } = &config.features;\n    let span_events = span_events\n        .as_ref()\n        .map_or(FmtSpan::NONE, ToOwned::to_owned);\n\n    // Configure features\n    layer = layer\n        .with_file(*file_names)\n        .with_line_number(*line_numbers)\n        .with_thread_names(*thread_names)\n        .with_thread_ids(*thread_ids)\n        .with_span_events(span_events)\n        .with_target(*target_display);\n\n    // Configure timer and writer\n    match timer {\n        LogTimer::None => configure_writer(layer.without_time(), &config.writer),\n        LogTimer::Time => configure_writer(layer.with_timer(time()), &config.writer),\n        LogTimer::Uptime => configure_writer(layer.with_timer(uptime()), &config.writer),\n    }\n}\n\nfn configure_writer<S, N, L, T, W>(\n    layer: fmt::Layer<S, N, fmt::format::Format<L, T>, W>,\n    writer: &WriterConfig,\n) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n    N: for<'writer> fmt::FormatFields<'writer> + Send + Sync + 'static,\n    L: Send + Sync + 'static,\n    T: Send + Sync + 'static,\n    fmt::format::Format<L, T>: fmt::FormatEvent<S, N>,\n{\n    match writer {\n        WriterConfig::Stdout => Ok(Box::new(layer.with_writer(std::io::stdout))),\n        WriterConfig::Stderr => Ok(Box::new(layer.with_writer(std::io::stderr))),\n        WriterConfig::File(path) => {\n            let file = std::fs::OpenOptions::new()\n                .create(true)\n                .append(true)\n                .open(path)?;\n            Ok(Box::new(layer.with_writer(file)))\n        }\n    }\n}\n\n/// Builder for pretty-formatted logs (development style)\n#[derive(Debug, Default, Clone)]\npub struct PrettyLayerBuilder;\n\nimpl LayerBuilder for PrettyLayerBuilder {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        let layer = tracing_subscriber::fmt::layer().pretty();\n\n        configure_layer(layer, config)\n    }\n}\n\n/// Builder for JSON-formatted logs (production style)\n#[derive(Debug, Default, Clone)]\npub struct JsonLayerBuilder;\n\nimpl LayerBuilder for JsonLayerBuilder {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        let layer = tracing_subscriber::fmt::layer().json();\n\n        configure_layer(layer, config)\n    }\n}\n\n/// Builder for full-formatted logs (default `tracing` style)\n#[derive(Debug, Default, Clone)]\npub struct FullLayerBuilder;\n\nimpl LayerBuilder for FullLayerBuilder {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        let layer = tracing_subscriber::fmt::layer();\n\n        configure_layer(layer, config)\n    }\n}\n\n/// Builder for compact-formatted logs (minimal style)\n#[derive(Debug, Default, Clone)]\npub struct CompactLayerBuilder;\n\nimpl LayerBuilder for CompactLayerBuilder {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        let layer = tracing_subscriber::fmt::layer().compact();\n\n        configure_layer(layer, config)\n    }\n}\n\n/// Builder for logfmt-formatted logs\n#[cfg(feature = \"logfmt\")]\n#[derive(Debug, Default, Clone)]\npub struct LogfmtLayerBuilder;\n\n#[cfg(feature = \"logfmt\")]\nimpl LayerBuilder for LogfmtLayerBuilder {\n    fn build_layer<S>(\n        &self,\n        config: &TracingConfig,\n    ) -> Result<Box<dyn Layer<S> + Send + Sync + 'static>, Error>\n    where\n        S: Subscriber + for<'a> LookupSpan<'a>,\n    {\n        // Note: tracing_logfmt doesn't support the same configuration options\n        // as the standard fmt layer, so we have limited configuration ability\n\n        if let WriterConfig::Stdout = &config.writer {\n            // Default behavior uses stdout\n            Ok(Box::new(tracing_logfmt::layer()))\n        } else {\n            // For stderr, we need to use the builder pattern since layer() doesn't support with_writer\n            // However, the current tracing_logfmt version may not support this\n            // For now, we'll fall back to the basic layer\n            tracing::warn!(\"logfmt only support stdout\");\n            Ok(Box::new(tracing_logfmt::layer()))\n        }\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/lib.rs",
    "content": "//#![warn(missing_docs)]\n#![forbid(unsafe_code)]\n#![warn(clippy::perf)]\n#![warn(clippy::pedantic)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::missing_errors_doc)]\n#![doc = include_str!(\"../README.md\")]\n#![cfg_attr(docs_rs, feature(doc_cfg))]\n\npub use opentelemetry_sdk;\npub use tracing_opentelemetry;\n\nmod error;\npub use error::Error;\n\nuse opentelemetry::propagation::{TextMapCompositePropagator, TextMapPropagator};\nuse opentelemetry_sdk::propagation::{BaggagePropagator, TraceContextPropagator};\nuse opentelemetry_sdk::trace::TraceError;\n\n#[cfg(feature = \"tracing_subscriber_ext\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"tracing_subscriber_ext\")))]\npub mod config;\n#[cfg(feature = \"tracing_subscriber_ext\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"tracing_subscriber_ext\")))]\npub mod formats;\n#[cfg(feature = \"otlp\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"otlp\")))]\npub mod otlp;\n#[cfg(feature = \"tracer\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"tracer\")))]\npub mod resource;\n#[cfg(feature = \"stdout\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"stdout\")))]\npub mod stdio;\n#[cfg(feature = \"tracing_subscriber_ext\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"tracing_subscriber_ext\")))]\npub mod tracing_subscriber_ext;\n\n/// Configure the global propagator based on content of the env variable [OTEL_PROPAGATORS](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_propagators)\n/// Specifies Propagators to be used in a comma-separated list.\n/// Default value: `\"tracecontext,baggage\"`\n/// Example: `export OTEL_PROPAGATORS=\"b3\"`\n/// Accepted values for `OTEL_PROPAGATORS` are:\n///\n/// - \"tracecontext\": W3C Trace Context\n/// - \"baggage\": W3C Baggage\n/// - \"b3\": B3 Single (require feature \"zipkin\")\n/// - \"b3multi\": B3 Multi (require feature \"zipkin\")\n/// - \"jaeger\": Jaeger (require feature \"jaeger\")\n/// - \"xray\": AWS X-Ray (require feature \"xray\")\n/// - \"ottrace\": OT Trace (third party) (not supported)\n/// - \"none\": No automatically configured propagator.\n///\n/// # Errors\n///\n/// Will return `TraceError` if issue in reading or instanciate propagator.\npub fn init_propagator() -> Result<(), TraceError> {\n    let value_from_env =\n        std::env::var(\"OTEL_PROPAGATORS\").unwrap_or_else(|_| \"tracecontext,baggage\".to_string());\n    let propagators: Vec<(Box<dyn TextMapPropagator + Send + Sync>, String)> = value_from_env\n        .split(',')\n        .map(|s| {\n            let name = s.trim().to_lowercase();\n            propagator_from_string(&name).map(|o| o.map(|b| (b, name)))\n        })\n        .collect::<Result<Vec<_>, _>>()?\n        .into_iter()\n        .flatten()\n        .collect();\n    if !propagators.is_empty() {\n        let (propagators_impl, propagators_name): (Vec<_>, Vec<_>) =\n            propagators.into_iter().unzip();\n        tracing::debug!(target: \"otel::setup\", OTEL_PROPAGATORS = propagators_name.join(\",\"));\n        let composite_propagator = TextMapCompositePropagator::new(propagators_impl);\n        opentelemetry::global::set_text_map_propagator(composite_propagator);\n    }\n    Ok(())\n}\n\n#[allow(clippy::box_default)]\nfn propagator_from_string(\n    v: &str,\n) -> Result<Option<Box<dyn TextMapPropagator + Send + Sync>>, TraceError> {\n    match v {\n        \"tracecontext\" => Ok(Some(Box::new(TraceContextPropagator::new()))),\n        \"baggage\" => Ok(Some(Box::new(BaggagePropagator::new()))),\n        #[cfg(feature = \"zipkin\")]\n        \"b3\" => Ok(Some(Box::new(\n            opentelemetry_zipkin::Propagator::with_encoding(\n                opentelemetry_zipkin::B3Encoding::SingleHeader,\n            ),\n        ))),\n        #[cfg(not(feature = \"zipkin\"))]\n        \"b3\" => Err(TraceError::from(\n            \"unsupported propagators form env OTEL_PROPAGATORS: 'b3', try to enable compile feature 'zipkin'\",\n        )),\n        #[cfg(feature = \"zipkin\")]\n        \"b3multi\" => Ok(Some(Box::new(\n            opentelemetry_zipkin::Propagator::with_encoding(\n                opentelemetry_zipkin::B3Encoding::MultipleHeader,\n            ),\n        ))),\n        #[cfg(not(feature = \"zipkin\"))]\n        \"b3multi\" => Err(TraceError::from(\n            \"unsupported propagators form env OTEL_PROPAGATORS: 'b3multi', try to enable compile feature 'zipkin'\",\n        )),\n        #[cfg(feature = \"jaeger\")]\n        \"jaeger\" => Ok(Some(Box::new(\n            opentelemetry_jaeger_propagator::Propagator::default(),\n        ))),\n        #[cfg(not(feature = \"jaeger\"))]\n        \"jaeger\" => Err(TraceError::from(\n            \"unsupported propagators form env OTEL_PROPAGATORS: 'jaeger', try to enable compile feature 'jaeger'\",\n        )),\n        #[cfg(feature = \"xray\")]\n        \"xray\" => Ok(Some(Box::new(\n            opentelemetry_aws::trace::XrayPropagator::default(),\n        ))),\n        #[cfg(not(feature = \"xray\"))]\n        \"xray\" => Err(TraceError::from(\n            \"unsupported propagators form env OTEL_PROPAGATORS: 'xray', try to enable compile feature 'xray'\",\n        )),\n        \"none\" => Ok(None),\n        unknown => Err(TraceError::from(format!(\n            \"unsupported propagators form env OTEL_PROPAGATORS: '{unknown}'\"\n        ))),\n    }\n}\n\n// Re-export the new configuration API for easier access\n#[cfg(feature = \"tracing_subscriber_ext\")]\npub use config::{\n    FeatureSet, Guard, LevelConfig, LogFormat, LogTimer, OtelConfig, TracingConfig, WriterConfig,\n};\n\n#[cfg(feature = \"tracing_subscriber_ext\")]\npub use formats::{\n    CompactLayerBuilder, FullLayerBuilder, JsonLayerBuilder, LayerBuilder, PrettyLayerBuilder,\n};\n\n#[cfg(all(feature = \"tracing_subscriber_ext\", feature = \"logfmt\"))]\npub use formats::LogfmtLayerBuilder;\n\n#[cfg(test)]\n#[cfg(feature = \"tracer\")]\nmod tests {\n    use assert2::assert;\n\n    #[test]\n    fn init_tracing_failed_on_invalid_propagator() {\n        assert!(let Err(_) = super::propagator_from_string(\"xxxxxx\"));\n\n        // std::env::set_var(\"OTEL_PROPAGATORS\", \"xxxxxx\");\n        // dbg!(std::env::var(\"OTEL_PROPAGATORS\"));\n        // assert!(let Err(_) = init_tracing());\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/otlp/logs.rs",
    "content": "use super::infer_protocol_from_env;\nuse opentelemetry_otlp::{ExporterBuildError, LogExporter};\nuse opentelemetry_sdk::{Resource, logs::LoggerProviderBuilder, logs::SdkLoggerProvider};\n#[cfg(feature = \"tls\")]\nuse {opentelemetry_otlp::WithTonicConfig, tonic::transport::ClientTlsConfig};\n\n#[must_use]\npub fn identity(v: LoggerProviderBuilder) -> LoggerProviderBuilder {\n    v\n}\n\npub fn init_loggerprovider<F>(\n    resource: Resource,\n    transform: F,\n) -> Result<SdkLoggerProvider, ExporterBuildError>\nwhere\n    F: FnOnce(LoggerProviderBuilder) -> LoggerProviderBuilder,\n{\n    let protocol = infer_protocol_from_env(\n        \"OTEL_EXPORTER_OTLP_LOGS_PROTOCOL\",\n        \"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT\",\n        \"v1/logs\",\n    );\n\n    // builders used the environment variables to determine the endpoint (but not to setup the protocol)\n    let exporter: Option<LogExporter> = match protocol.as_deref() {\n        Some(\"http/protobuf\") => Some(LogExporter::builder().with_http().build()?),\n        #[cfg(feature = \"tls\")]\n        Some(\"grpc/tls\") => Some(\n            LogExporter::builder()\n                .with_tonic()\n                .with_tls_config(ClientTlsConfig::new().with_enabled_roots())\n                .build()?,\n        ),\n        Some(\"grpc\") => Some(LogExporter::builder().with_tonic().build()?),\n        Some(x) => {\n            tracing::warn!(\n                \"unknown '{x}' env var set or infered for OTEL_EXPORTER_OTLP_LOGS_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no log exporter will be created\"\n            );\n            None\n        }\n        None => {\n            tracing::warn!(\n                \"no env var set or infered for OTEL_EXPORTER_OTLP_LOGS_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no log exporter will be created\"\n            );\n            None\n        }\n    };\n    let mut logger_provider = SdkLoggerProvider::builder().with_resource(resource);\n    if let Some(exporter) = exporter {\n        logger_provider = logger_provider.with_batch_exporter(exporter);\n    }\n\n    logger_provider = transform(logger_provider);\n    Ok(logger_provider.build())\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/otlp/metrics.rs",
    "content": "use super::infer_protocol_from_env;\nuse opentelemetry_otlp::{ExporterBuildError, MetricExporter, WithExportConfig};\nuse opentelemetry_sdk::Resource;\nuse opentelemetry_sdk::metrics::{\n    MeterProviderBuilder, PeriodicReader, SdkMeterProvider, Temporality,\n};\nuse std::env;\nuse std::time::Duration;\n#[cfg(feature = \"tls\")]\nuse {opentelemetry_otlp::WithTonicConfig, tonic::transport::ClientTlsConfig};\n\n#[must_use]\npub fn identity(v: MeterProviderBuilder) -> MeterProviderBuilder {\n    v\n}\n\npub fn init_meterprovider<F>(\n    resource: Resource,\n    transform: F,\n) -> Result<SdkMeterProvider, ExporterBuildError>\nwhere\n    F: FnOnce(MeterProviderBuilder) -> MeterProviderBuilder,\n{\n    let protocol = infer_protocol_from_env(\n        \"OTEL_EXPORTER_OTLP_METRICS_PROTOCOL\",\n        \"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT\",\n        \"v1/metrics\",\n    );\n    let timeout = env::var(\"OTEL_EXPORTER_OTLP_METRICS_TIMEOUT\")\n        .ok()\n        .and_then(|var| var.parse::<u64>().ok())\n        .map_or(Duration::from_secs(10), Duration::from_secs);\n    let temporality = env::var(\"OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE\")\n        .ok()\n        .and_then(|var| match var.to_lowercase().as_str() {\n            \"delta\" => Some(Temporality::Delta),\n            \"cumulative\" => Some(Temporality::Cumulative),\n            unknown => {\n                tracing::warn!(\"unknown '{unknown}' env var set for OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY; defaulting to cumulative\");\n                None\n            },\n        })\n        .unwrap_or_default();\n    let export_interval = env::var(\"OTEL_METRIC_EXPORT_INTERVAL\")\n        .ok()\n        .and_then(|var| var.parse::<u64>().ok())\n        .map_or(Duration::from_secs(60), Duration::from_millis);\n\n    // builders used the environment variables to determine the endpoint (but not to setup the protocol)\n    let exporter = match protocol.as_deref() {\n        Some(\"http/protobuf\") => Some(\n            MetricExporter::builder()\n                .with_http()\n                .with_temporality(temporality)\n                .with_timeout(timeout)\n                .build()?,\n        ),\n        #[cfg(feature = \"tls\")]\n        Some(\"grpc/tls\") => Some(\n            MetricExporter::builder()\n                .with_tonic()\n                .with_tls_config(ClientTlsConfig::new().with_enabled_roots())\n                .with_temporality(temporality)\n                .with_timeout(timeout)\n                .build()?,\n        ),\n        Some(\"grpc\") => Some(\n            MetricExporter::builder()\n                .with_tonic()\n                .with_temporality(temporality)\n                .with_timeout(timeout)\n                .build()?,\n        ),\n        Some(x) => {\n            tracing::warn!(\n                \"unknown '{x}' env var set or infered for OTEL_EXPORTER_OTLP_METRICS_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no metric exporter will be created\"\n            );\n            None\n        }\n        None => {\n            tracing::warn!(\n                \"no env var set or infered for OTEL_EXPORTER_OTLP_METRICS_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no metric exporter will be created\"\n            );\n            None\n        }\n    };\n    let mut meter_provider = SdkMeterProvider::builder().with_resource(resource);\n    if let Some(exporter) = exporter {\n        let reader = PeriodicReader::builder(exporter)\n            .with_interval(export_interval)\n            .build();\n        meter_provider = meter_provider.with_reader(reader);\n    }\n    meter_provider = transform(meter_provider);\n    Ok(meter_provider.build())\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/otlp/mod.rs",
    "content": "#[cfg(feature = \"logs\")]\npub mod logs;\n#[cfg(feature = \"metrics\")]\npub mod metrics;\npub mod traces;\n\n#[cfg(feature = \"logs\")]\nuse opentelemetry::logs::LoggerProvider;\n#[cfg(feature = \"metrics\")]\nuse opentelemetry::metrics::MeterProvider;\n#[cfg(feature = \"logs\")]\nuse opentelemetry_sdk::logs::SdkLoggerProvider;\n#[cfg(feature = \"metrics\")]\nuse opentelemetry_sdk::metrics::SdkMeterProvider;\n\nuse opentelemetry::trace::TracerProvider;\nuse opentelemetry_sdk::trace::SdkTracerProvider;\n\n#[must_use = \"Recommend holding with 'let _guard = ' pattern to ensure final traces/logs/metrics are sent to the server\"]\n/// On Drop of the `OtelGuard` instance,\n/// the wrapped Tracer/Logger/Meter Provider is force to flush and to shutdown (ignoring error).\n#[allow(clippy::struct_field_names)]\npub struct OtelGuard {\n    #[cfg(feature = \"logs\")]\n    pub(crate) logger_provider: SdkLoggerProvider,\n    #[cfg(feature = \"metrics\")]\n    pub(crate) meter_provider: SdkMeterProvider,\n    pub(crate) tracer_provider: SdkTracerProvider,\n}\n\nimpl OtelGuard {\n    #[cfg(feature = \"logs\")]\n    #[must_use]\n    pub fn logger_provider(&self) -> &impl LoggerProvider {\n        &self.logger_provider\n    }\n\n    #[must_use]\n    pub fn tracer_provider(&self) -> &impl TracerProvider {\n        &self.tracer_provider\n    }\n\n    #[cfg(feature = \"metrics\")]\n    #[must_use]\n    pub fn meter_provider(&self) -> &impl MeterProvider {\n        &self.meter_provider\n    }\n}\n\nimpl Drop for OtelGuard {\n    #[allow(unused_must_use)]\n    fn drop(&mut self) {\n        let _ = self.tracer_provider.force_flush();\n        let _ = self.tracer_provider.shutdown();\n        #[cfg(feature = \"logs\")]\n        {\n            let _ = self.logger_provider.force_flush();\n            let _ = self.logger_provider.shutdown();\n        }\n        #[cfg(feature = \"metrics\")]\n        {\n            let _ = self.meter_provider.force_flush();\n            let _ = self.meter_provider.shutdown();\n        }\n    }\n}\n\n#[allow(unused_mut)]\npub(crate) fn infer_protocol_from_env(\n    protocol_key: &str,\n    endpoint_key: &str,\n    endpoint_path: &str,\n) -> Option<String> {\n    let (maybe_protocol, maybe_endpoint) =\n        read_protocol_and_endpoint_from_env(protocol_key, endpoint_key, endpoint_path);\n    infer_protocol(maybe_protocol.as_deref(), maybe_endpoint.as_deref())\n}\n\n#[allow(unused_mut)]\nfn infer_protocol(maybe_protocol: Option<&str>, maybe_endpoint: Option<&str>) -> Option<String> {\n    let mut maybe_protocol = match (maybe_protocol, maybe_endpoint) {\n        (Some(protocol), _) => Some(protocol.to_string()),\n        (None, Some(endpoint)) => {\n            if endpoint.contains(\":4317\") {\n                Some(\"grpc\".to_string())\n            } else {\n                Some(\"http/protobuf\".to_string())\n            }\n        }\n        _ => None,\n    };\n    #[cfg(feature = \"tls\")]\n    if maybe_protocol.as_deref() == Some(\"grpc\")\n        && maybe_endpoint.is_some_and(|e| e.starts_with(\"https\"))\n    {\n        maybe_protocol = Some(\"grpc/tls\".to_string());\n    }\n\n    maybe_protocol\n}\n\npub fn debug_env() {\n    const SENSITIVE_KEYS: &[&str] = &[\n        \"OTEL_EXPORTER_OTLP_HEADERS\",\n        \"OTEL_EXPORTER_OTLP_CERTIFICATE\",\n    ];\n    std::env::vars()\n        .filter(|(k, _)| k.starts_with(\"OTEL_\"))\n        .for_each(|(k, v)| {\n            let display_value = if SENSITIVE_KEYS.iter().any(|s| k == *s) {\n                \"[redacted]\"\n            } else {\n                &v\n            };\n            tracing::debug!(target: \"otel::setup::env\", key = %k, value = %display_value);\n        });\n}\n\nfn read_protocol_and_endpoint_from_env(\n    protocol_key: &str,\n    endpoint_key: &str,\n    endpoint_path: &str,\n) -> (Option<String>, Option<String>) {\n    let maybe_protocol = std::env::var(protocol_key)\n        .or_else(|_| std::env::var(\"OTEL_EXPORTER_OTLP_PROTOCOL\"))\n        .ok();\n    let maybe_endpoint = std::env::var(endpoint_key)\n        .or_else(|_| {\n            std::env::var(\"OTEL_EXPORTER_OTLP_ENDPOINT\").map(|endpoint| match &maybe_protocol {\n                Some(protocol) if protocol.contains(\"http\") => {\n                    format!(\"{endpoint}/{endpoint_path}\")\n                }\n                _ => endpoint,\n            })\n        })\n        .ok();\n    (maybe_protocol, maybe_endpoint)\n}\n\n#[cfg(test)]\nmod tests {\n    use assert2::assert;\n    use rstest::rstest;\n\n    use super::*;\n\n    #[rstest]\n    #[case(None, None, None)] //Devskim: ignore DS137138\n    #[case(Some(\"http/protobuf\"), None, Some(\"http/protobuf\"))] //Devskim: ignore DS137138\n    #[case(Some(\"grpc\"), None, Some(\"grpc\"))] //Devskim: ignore DS137138\n    #[case(None, Some(\"http://localhost:4317\"), Some(\"grpc\"))] //Devskim: ignore DS137138\n    #[cfg_attr(\n        feature = \"tls\",\n        case(None, Some(\"https://localhost:4317\"), Some(\"grpc/tls\"))\n    )]\n    #[cfg_attr(\n        feature = \"tls\",\n        case(Some(\"grpc/tls\"), Some(\"https://localhost:4317\"), Some(\"grpc/tls\"))\n    )]\n    #[case(\n        Some(\"http/protobuf\"),\n        Some(\"http://localhost:4318/v1/traces\"), //Devskim: ignore DS137138\n        Some(\"http/protobuf\"),\n    )]\n    #[case(\n        Some(\"http/protobuf\"),\n        Some(\"https://examples.com:4318/v1/traces\"),\n        Some(\"http/protobuf\")\n    )]\n    #[case(\n        Some(\"http/protobuf\"),\n        Some(\"https://examples.com:4317\"),\n        Some(\"http/protobuf\")\n    )]\n    fn test_infer_protocol(\n        #[case] traces_protocol: Option<&str>,\n        #[case] traces_endpoint: Option<&str>,\n        #[case] expected_protocol: Option<&str>,\n    ) {\n        assert!(infer_protocol(traces_protocol, traces_endpoint).as_deref() == expected_protocol);\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/otlp/traces.rs",
    "content": "use super::infer_protocol_from_env;\nuse opentelemetry_otlp::{ExporterBuildError, SpanExporter};\nuse opentelemetry_sdk::{Resource, trace::SdkTracerProvider, trace::TracerProviderBuilder};\n#[cfg(feature = \"tls\")]\nuse {opentelemetry_otlp::WithTonicConfig, tonic::transport::ClientTlsConfig};\n\n#[must_use]\npub fn identity(v: TracerProviderBuilder) -> TracerProviderBuilder {\n    v\n}\n\n// see https://opentelemetry.io/docs/reference/specification/protocol/exporter/\npub fn init_tracerprovider<F>(\n    resource: Resource,\n    transform: F,\n) -> Result<SdkTracerProvider, ExporterBuildError>\nwhere\n    F: FnOnce(TracerProviderBuilder) -> TracerProviderBuilder,\n{\n    super::debug_env();\n    let protocol = infer_protocol_from_env(\n        \"OTEL_EXPORTER_OTLP_TRACES_PROTOCOL\",\n        \"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT\",\n        \"v1/traces\",\n    );\n\n    // builders used the environment variables to determine the endpoint (but not to setup the protocol)\n    let exporter: Option<SpanExporter> = match protocol.as_deref() {\n        Some(\"http/protobuf\") => Some(SpanExporter::builder().with_http().build()?),\n        #[cfg(feature = \"tls\")]\n        Some(\"grpc/tls\") => Some(\n            SpanExporter::builder()\n                .with_tonic()\n                .with_tls_config(ClientTlsConfig::new().with_enabled_roots())\n                .build()?,\n        ),\n        Some(\"grpc\") => Some(SpanExporter::builder().with_tonic().build()?),\n        Some(x) => {\n            tracing::warn!(\n                \"unknown '{x}' env var set or infered for OTEL_EXPORTER_OTLP_TRACES_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no span exporter will be created\"\n            );\n            None\n        }\n        None => {\n            tracing::warn!(\n                \"no env var set or infered for OTEL_EXPORTER_OTLP_TRACES_PROTOCOL or OTEL_EXPORTER_OTLP_PROTOCOL; no span exporter will be created\"\n            );\n            None\n        }\n    };\n    let mut trace_provider = SdkTracerProvider::builder().with_resource(resource);\n    if let Some(exporter) = exporter {\n        trace_provider = trace_provider.with_batch_exporter(exporter);\n    }\n\n    trace_provider = transform(trace_provider);\n    Ok(trace_provider.build())\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/resource.rs",
    "content": "use std::borrow::Cow;\n\nuse opentelemetry::KeyValue;\n// use opentelemetry_resource_detectors::OsResourceDetector;\nuse opentelemetry_sdk::{Resource, resource::ResourceDetector};\nuse opentelemetry_semantic_conventions::resource;\n\n/// To log detected value set environement variable `RUST_LOG=\"...,otel::setup::resource=debug\"`\n/// ```rust\n/// use init_tracing_opentelemetry::resource::DetectResource;\n/// # fn main() {\n/// let otel_rsrc = DetectResource::default()\n///     .with_fallback_service_name(env!(\"CARGO_PKG_NAME\"))\n///     .with_fallback_service_version(env!(\"CARGO_PKG_VERSION\"))\n///     .build();\n/// # }\n///\n/// ```\n#[derive(Debug, Default, Clone)]\npub struct DetectResource {\n    fallback_service_name: Option<Cow<'static, str>>,\n    fallback_service_version: Option<Cow<'static, str>>,\n}\n\nimpl DetectResource {\n    /// `service.name` is first extracted from environment variables\n    /// (in this order) `OTEL_SERVICE_NAME`, `SERVICE_NAME`, `APP_NAME`.\n    /// But a default value can be provided with this method.\n    #[must_use]\n    pub fn with_fallback_service_name(\n        mut self,\n        fallback_service_name: impl Into<Cow<'static, str>>,\n    ) -> Self {\n        self.fallback_service_name = Some(fallback_service_name.into());\n        self\n    }\n\n    /// `service.name` is first extracted from environment variables\n    /// (in this order) `SERVICE_VERSION`, `APP_VERSION`.\n    /// But a default value can be provided with this method.\n    #[must_use]\n    pub fn with_fallback_service_version(\n        mut self,\n        fallback_service_version: impl Into<Cow<'static, str>>,\n    ) -> Self {\n        self.fallback_service_version = Some(fallback_service_version.into());\n        self\n    }\n\n    #[must_use]\n    pub fn build(self) -> Resource {\n        //Box::new(OsResourceDetector), //FIXME enable when available for opentelemetry >= 0.25\n        //Box::new(ProcessResourceDetector),\n        let rsrc = Resource::builder()\n            .with_detector(Box::new(ServiceInfoDetector {\n                fallback_service_name: self.fallback_service_name,\n                fallback_service_version: self.fallback_service_version,\n            }))\n            .build();\n        debug_resource(&rsrc);\n        rsrc\n    }\n}\n\npub fn debug_resource(rsrc: &Resource) {\n    rsrc.iter().for_each(\n        |kv| tracing::debug!(target: \"otel::setup::resource\", key = %kv.0, value = %kv.1),\n    );\n}\n\n#[derive(Debug)]\npub struct ServiceInfoDetector {\n    fallback_service_name: Option<Cow<'static, str>>,\n    fallback_service_version: Option<Cow<'static, str>>,\n}\n\nimpl ResourceDetector for ServiceInfoDetector {\n    fn detect(&self) -> Resource {\n        let service_name = std::env::var(\"OTEL_SERVICE_NAME\")\n            .or_else(|_| std::env::var(\"SERVICE_NAME\"))\n            .or_else(|_| std::env::var(\"APP_NAME\"))\n            .ok()\n            .or_else(|| self.fallback_service_name.clone().map(|v| v.to_string()))\n            .map(|v| KeyValue::new(resource::SERVICE_NAME, v));\n        let service_version = std::env::var(\"SERVICE_VERSION\")\n            .or_else(|_| std::env::var(\"APP_VERSION\"))\n            .ok()\n            .or_else(|| {\n                self.fallback_service_version\n                    .clone()\n                    .map(std::borrow::Cow::into_owned)\n            })\n            .map(|v| KeyValue::new(resource::SERVICE_VERSION, v));\n        let mut resource = Resource::builder_empty();\n        if let Some(service_name) = service_name {\n            resource = resource.with_attribute(service_name);\n        }\n        if let Some(service_version) = service_version {\n            resource = resource.with_attribute(service_version);\n        }\n        resource.build()\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/stdio.rs",
    "content": "use crate::Error;\nuse opentelemetry::InstrumentationScope;\nuse opentelemetry::trace::TracerProvider as _;\nuse opentelemetry_sdk::Resource;\nuse opentelemetry_sdk::trace as sdktrace;\nuse opentelemetry_sdk::trace::BatchSpanProcessor;\nuse opentelemetry_sdk::trace::SdkTracerProvider;\nuse opentelemetry_sdk::trace::TracerProviderBuilder;\nuse std::fmt::Debug;\nuse std::io::Write;\n\n#[must_use]\npub fn identity<W: Write>(v: TracerProviderBuilder) -> TracerProviderBuilder {\n    v\n}\n\npub fn init_tracer<F, W>(resource: Resource, transform: F) -> Result<sdktrace::Tracer, Error>\nwhere\n    F: FnOnce(TracerProviderBuilder) -> TracerProviderBuilder,\n    W: Write + Debug + Send + Sync + 'static,\n{\n    let exporter = opentelemetry_stdout::SpanExporter::default();\n    let processor = BatchSpanProcessor::builder(exporter).build();\n    let mut provider_builder = SdkTracerProvider::builder()\n        .with_span_processor(processor)\n        .with_resource(resource)\n        .with_sampler(sdktrace::Sampler::AlwaysOn);\n    provider_builder = transform(provider_builder);\n    // tracer used in libraries/crates that optionally includes version and schema url\n    let scope = InstrumentationScope::builder(env!(\"CARGO_PKG_NAME\"))\n        .with_version(env!(\"CARGO_PKG_VERSION\"))\n        .with_schema_url(\"https://opentelemetry.io/schema/1.0.0\")\n        .build();\n    Ok(provider_builder.build().tracer_with_scope(scope))\n}\n\n#[derive(Debug, Default)]\npub struct WriteNoWhere;\n\nimpl Write for WriteNoWhere {\n    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "init-tracing-opentelemetry/src/tracing_subscriber_ext.rs",
    "content": "#![allow(deprecated)]\nuse std::borrow::Cow;\n\nuse opentelemetry::trace::TracerProvider;\n#[cfg(feature = \"logs\")]\nuse opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;\n#[cfg(feature = \"logs\")]\nuse opentelemetry_sdk::logs::{SdkLogger, SdkLoggerProvider};\n#[cfg(feature = \"metrics\")]\nuse opentelemetry_sdk::metrics::SdkMeterProvider;\nuse opentelemetry_sdk::{\n    Resource,\n    trace::{SdkTracerProvider, Tracer},\n};\nuse tracing::{Subscriber, level_filters::LevelFilter};\n#[cfg(feature = \"metrics\")]\nuse tracing_opentelemetry::MetricsLayer;\nuse tracing_opentelemetry::OpenTelemetryLayer;\nuse tracing_subscriber::{Layer, filter::EnvFilter, layer::SubscriberExt, registry::LookupSpan};\n\nuse crate::{\n    Error,\n    config::TracingConfig,\n    init_propagator, //stdio,\n    otlp,\n    otlp::OtelGuard,\n    resource::DetectResource,\n};\n\n#[must_use]\n#[deprecated(\n    since = \"0.31.0\",\n    note = \"Use `TracingConfig::default().build_layer()` instead\"\n)]\n/// # Panics\n/// Panics if the logger layer cannot be built.\npub fn build_logger_text<S>() -> Box<dyn Layer<S> + Send + Sync + 'static>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n{\n    TracingConfig::default()\n        .build_layer()\n        .expect(\"Failed to build logger layer\")\n}\n\n#[must_use]\n#[deprecated = \"replaced by the configurable build_level_filter_layer(\\\"\\\")\"]\npub fn build_loglevel_filter_layer() -> EnvFilter {\n    build_level_filter_layer(\"\").unwrap_or_default()\n}\n\n/// Read the configuration from (first non empty used, priority top to bottom):\n///\n/// - from parameter `directives`\n/// - from environment variable `RUST_LOG`\n/// - from environment variable `OTEL_LOG_LEVEL`\n/// - default to `Level::INFO`\n///\n/// And add directive to:\n///\n/// - `otel::tracing` should be a level info to emit opentelemetry trace & span\n///\n/// You can customize parameter \"directives\", by adding:\n///\n/// - `otel::setup=debug` set to debug to log detected resources, configuration read (optional)\n///\n/// see [Directives syntax](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives)\npub fn build_level_filter_layer(log_directives: &str) -> Result<EnvFilter, Error> {\n    let dirs = if log_directives.is_empty() {\n        std::env::var(\"RUST_LOG\")\n            .or_else(|_| std::env::var(\"OTEL_LOG_LEVEL\"))\n            .unwrap_or_else(|_| \"info\".to_string())\n    } else {\n        log_directives.to_string()\n    };\n    let directive_to_allow_otel_trace = \"otel::tracing=trace\".parse()?;\n\n    Ok(EnvFilter::builder()\n        .with_default_directive(LevelFilter::INFO.into())\n        .parse_lossy(dirs)\n        .add_directive(directive_to_allow_otel_trace))\n}\n\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\npub fn regiter_otel_layers<S>(\n    subscriber: S,\n) -> Result<(impl Subscriber + for<'span> LookupSpan<'span>, OtelGuard), Error>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n{\n    register_otel_layers_with_resource(subscriber, DetectResource::default().build())\n}\n\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\npub fn register_otel_layers_with_resource<S>(\n    subscriber: S,\n    otel_rsrc: Resource,\n) -> Result<(impl Subscriber + for<'span> LookupSpan<'span>, OtelGuard), Error>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n{\n    #[cfg(feature = \"logs\")]\n    let (logs_layer, logger_provider) = build_logger_layer_with_resource(otel_rsrc.clone())?;\n    #[cfg(feature = \"metrics\")]\n    let (metrics_layer, meter_provider) = build_metrics_layer_with_resource(otel_rsrc.clone())?;\n    let (trace_layer, tracer_provider) = build_tracer_layer_with_resource(otel_rsrc)?;\n    let subscriber = subscriber.with(trace_layer);\n    #[cfg(feature = \"logs\")]\n    let subscriber = subscriber.with(logs_layer);\n    #[cfg(feature = \"metrics\")]\n    let subscriber = subscriber.with(metrics_layer);\n    Ok((\n        subscriber,\n        OtelGuard {\n            #[cfg(feature = \"logs\")]\n            logger_provider,\n            #[cfg(feature = \"metrics\")]\n            meter_provider,\n            tracer_provider,\n        },\n    ))\n}\n\n/// change (version 0.31): no longer set the global tracer\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\npub fn build_tracer_layer<S>() -> Result<(OpenTelemetryLayer<S, Tracer>, SdkTracerProvider), Error>\nwhere\n    S: Subscriber + for<'span> LookupSpan<'span>,\n{\n    build_tracer_layer_with_resource(\n        DetectResource::default()\n            //.with_fallback_service_name(env!(\"CARGO_PKG_NAME\"))\n            //.with_fallback_service_version(env!(\"CARGO_PKG_VERSION\"))\n            .build(),\n    )\n}\n\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\npub fn build_tracer_layer_with_resource<S>(\n    otel_rsrc: Resource,\n) -> Result<(OpenTelemetryLayer<S, Tracer>, SdkTracerProvider), Error>\nwhere\n    S: Subscriber + for<'span> LookupSpan<'span>,\n{\n    build_tracer_layer_with_resource_and_name(otel_rsrc, \"\")\n}\n\n#[cfg(feature = \"logs\")]\npub(crate) fn build_logger_layer_with_resource(\n    otel_rsrc: Resource,\n) -> Result<\n    (\n        OpenTelemetryTracingBridge<SdkLoggerProvider, SdkLogger>,\n        SdkLoggerProvider,\n    ),\n    crate::Error,\n> {\n    let logger_provider = otlp::logs::init_loggerprovider(otel_rsrc, otlp::logs::identity)?;\n    let layer = OpenTelemetryTracingBridge::new(&logger_provider);\n    Ok((layer, logger_provider))\n}\n\npub(crate) fn build_tracer_layer_with_resource_and_name<S>(\n    otel_rsrc: Resource,\n    tracer_name: impl Into<Cow<'static, str>>,\n) -> Result<(OpenTelemetryLayer<S, Tracer>, SdkTracerProvider), Error>\nwhere\n    S: Subscriber + for<'span> LookupSpan<'span>,\n{\n    let tracer_provider = otlp::traces::init_tracerprovider(otel_rsrc, otlp::traces::identity)?;\n    // to not send trace somewhere, but continue to create and propagate,...\n    // then send them to `init_tracing_opentelemetry::stdio::WriteNoWhere::default()`\n    // or to `std::io::stdout()` to print\n    //\n    // let otel_tracer = stdio::init_tracer(\n    //     otel_rsrc,\n    //     stdio::identity::<stdio::WriteNoWhere>,\n    //     stdio::WriteNoWhere::default(),\n    // )?;\n    init_propagator()?;\n    let layer = tracing_opentelemetry::layer()\n        .with_error_records_to_exceptions(true)\n        .with_tracer(tracer_provider.tracer(tracer_name));\n    opentelemetry::global::set_tracer_provider(tracer_provider.clone());\n    Ok((layer, tracer_provider))\n}\n\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\n#[cfg(feature = \"metrics\")]\npub fn build_metrics_layer<S>()\n-> Result<(MetricsLayer<S, SdkMeterProvider>, SdkMeterProvider), Error>\nwhere\n    S: Subscriber + for<'span> LookupSpan<'span>,\n{\n    build_metrics_layer_with_resource(DetectResource::default().build())\n}\n\n#[deprecated(since = \"0.31.0\", note = \"Use `TracingConfig` instead\")]\n#[cfg(feature = \"metrics\")]\npub fn build_metrics_layer_with_resource<S>(\n    otel_rsrc: Resource,\n) -> Result<(MetricsLayer<S, SdkMeterProvider>, SdkMeterProvider), Error>\nwhere\n    S: Subscriber + for<'a> LookupSpan<'a>,\n{\n    let meter_provider = otlp::metrics::init_meterprovider(otel_rsrc, otlp::metrics::identity)?;\n    let layer = MetricsLayer::new(meter_provider.clone());\n    opentelemetry::global::set_meter_provider(meter_provider.clone());\n    Ok((layer, meter_provider))\n}\n\n/// Initialize subscribers with default configuration\n///\n/// This is a convenience function that uses production-ready defaults.\n/// For more control, use `TracingConfig::production().init_subscriber()`.\n#[deprecated(\n    since = \"0.31.0\",\n    note = \"Use `TracingConfig::production()...` instead\"\n)]\npub fn init_subscribers() -> Result<OtelGuard, Error> {\n    let guard = TracingConfig::production().init_subscriber()?;\n    match guard.otel_guard {\n        Some(otel_guard) => {\n            // For backward compatibility, we leak the default_guard since the caller\n            // only expects an OtelGuard and won't hold onto the DefaultGuard\n            if let Some(default_guard) = guard.default_guard {\n                std::mem::forget(default_guard);\n            }\n            Ok(otel_guard)\n        }\n        None => Err(std::io::Error::new(\n            std::io::ErrorKind::Unsupported,\n            \"OpenTelemetry is disabled but OtelGuard was requested\",\n        )\n        .into()),\n    }\n}\n\n/// Initialize subscribers with custom log directives\n///\n/// See [`build_level_filter_layer`] for the syntax of `log_directives`.\n/// For more control, use `TracingConfig::production().with_log_directives(log_directives).init_subscriber()`.\n#[deprecated(\n    since = \"0.31.0\",\n    note = \"Use `TracingConfig::production().with_log_directives(log_directives)...` instead\"\n)]\npub fn init_subscribers_and_loglevel(log_directives: &str) -> Result<OtelGuard, Error> {\n    let guard = TracingConfig::production()\n        .with_log_directives(log_directives)\n        .init_subscriber()?;\n    match guard.otel_guard {\n        Some(otel_guard) => {\n            // For backward compatibility, we leak the default_guard since the caller\n            // only expects an OtelGuard and won't hold onto the DefaultGuard\n            if let Some(default_guard) = guard.default_guard {\n                std::mem::forget(default_guard);\n            }\n            Ok(otel_guard)\n        }\n        None => Err(std::io::Error::new(\n            std::io::ErrorKind::Unsupported,\n            \"OpenTelemetry is disabled but OtelGuard was requested\",\n        )\n        .into()),\n    }\n}\n"
  },
  {
    "path": "release-plz.toml",
    "content": "# [Configuration | Release-plz](https://release-plz.ieni.dev/docs/config)\n[workspace]\nfeatures_always_increment_minor = true\n\n[changelog]\nsort_commits = \"newest\"\ncommit_preprocessors = [\n    # { pattern = '\\((\\w+\\s)?#([0-9]+)\\)', replace = \"\" }, # remove issue numbers from commits\n    # { pattern = '\\((\\w+\\s)?#([0-9]+)\\)', replace = \"([#${2}](<REPO>/issues/${2}))\"}, # replace issue numbers\n]\n# regex for parsing and grouping commits\n# try to follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)\ncommit_parsers = [\n    { message = \"^(🔒️|🔐)\", group = \"<!-- 0 -->Security\" },\n    { body = \".*security\", group = \"<!-- 0 -->Security\" },\n    { message = \"^(fix|🐛|🚑️|👽️)\", group = \"<!-- 1 -->Fixed\" },\n    { message = \"^(test|✅)\", group = \"<!-- 1 -->Fixed\", skip = true },\n    { message = \"^.*: add\", group = \"<!-- 2 -->Added\" },\n    { message = \"^.*: support\", group = \"<!-- 2 -->Added\" },\n    { message = \"^(feat|✨|💥)\", group = \"<!-- 2 -->Added\" },\n    { message = \"^.*: remove\", group = \"<!-- 3 -->Removed\" },\n    { message = \"^.*: delete\", group = \"<!-- 3 -->Removed\" },\n    { message = \"^(style|💄)\", group = \"<!-- 4 -->Changed\" },\n    { message = \"^(doc|✏️|📝)\", group = \"<!-- 4 -->Changed\" },\n    { message = \"^(perf|⚡️)\", group = \"<!-- 4 -->Changed\" },\n    { message = \"^(chore|ci|💚|👷|🚧)\", group = \"<!-- 4 -->Changed\", skip = true },\n    { message = \"^revert\", group = \"<!-- 4 -->Changed\" },\n    { message = \"^(chore\\\\(deps\\\\)|⬇️|⬆️|➕|➖)\", group = \"<!-- 4 -->Changed\" },\n    { message = \"^(refactor|🎨|🔥|♻️)\", group = \"<!-- 5 -->Refactor\", skip = true },\n    { message = \"^(chore\\\\(release\\\\): prepare for|🔖|🚀)\", skip = true },\n    { message = \"^chore\\\\(pr\\\\)\", skip = true },\n    { message = \"^chore\\\\(pull\\\\)\", skip = true },\n]\n"
  },
  {
    "path": "renovate.json5",
    "content": "// https://docs.renovatebot.com/configuration-options/\n// https://www.augmentedmind.de/2023/07/30/renovate-bot-cheat-sheet/\n{\n  $schema: \"https://docs.renovatebot.com/renovate-schema.json\",\n  extends: [\"config:recommended\"],\n  additionalReviewers: [\"davidB\"],\n  ignoreDeps: [ \"rust\" ], // to keep mise, rust-toolchain aligned to MSRV\n  packageRules: [\n    {\n      matchUpdateTypes: [\"patch\", \"pin\", \"digest\"],\n      enabled: false,\n    },\n    {\n      matchPackageNames: [\"helm\"],\n      automerge: true,\n      // Force Renovate to not create a PR (but merge its branches directly), to avoid PR-related email spam\n      automergeType: \"branch\",\n    },\n    {\n      matchPackageNames: [\"/opentelemetry/\"],\n      groupName: \"opentelemetry\",\n    },\n    {\n      matchPackageNames: [\"kube\", \"k8s-openapi\"],\n      groupName: \"kubers\",\n    },\n    {\n      matchPackageNames: [\"rust\"],\n      enabled: false,\n    },\n  ],\n}\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"1.91.0\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/Cargo.toml",
    "content": "[package]\nname = \"testing-tracing-opentelemetry\"\ndescription = \"helpers to help testing app + tracing + opentelemetry.\"\nreadme = \"README.md\"\nkeywords = [\"tracing\", \"opentelemetry\"]\ncategories = [\"development-tools::testing\"]\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/testing-tracing-opentelemetry\"\npublish = false\nedition.workspace = true\nversion = \"0.19.0\"\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\nassert2 = { workspace = true }\nfake-opentelemetry-collector = { path = \"../fake-opentelemetry-collector\", version = \"0.34\" }\ninsta = { workspace = true }\nopentelemetry = { workspace = true }\nopentelemetry_sdk = { workspace = true }\nserde_json = \"1\"\ntracing = { workspace = true }\ntracing-opentelemetry = { workspace = true }\ntracing-subscriber = { version = \"0.3\", default-features = false, features = [\n  \"env-filter\",\n  \"fmt\",\n  \"json\",\n] }\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/lib.rs",
    "content": "use assert2::{assert, check};\nuse opentelemetry::trace::TracerProvider;\nuse opentelemetry_sdk::propagation::TraceContextPropagator;\nuse serde_json::Value;\nuse std::sync::mpsc::{self, Receiver, SyncSender};\nuse tracing_subscriber::{\n    EnvFilter,\n    fmt::{MakeWriter, format::FmtSpan},\n    util::SubscriberInitExt,\n};\n\npub fn assert_trace(\n    name: &str,\n    tracing_events: Vec<Value>,\n    otel_spans: Vec<fake_opentelemetry_collector::ExportedSpan>,\n    is_trace_id_constant: bool,\n) {\n    let trace_id_0 = tracing_events\n        .first()\n        .and_then(|v| v.as_object())\n        .and_then(|v| v.get(\"span\"))\n        .and_then(|v| v.as_object())\n        .and_then(|v| v.get(\"trace_id\"))\n        .and_then(|v| v.as_str())\n        .unwrap_or_default()\n        .to_owned();\n    // let trace_id_3 = trace_id_0.clone();\n    let trace_id_1 = trace_id_0.clone();\n    let trace_id_2 = trace_id_0;\n    insta::assert_yaml_snapshot!(name, tracing_events, {\n        \"[].timestamp\" => \"[timestamp]\",\n        \"[].fields[\\\"time.busy\\\"]\" => \"[duration]\",\n        \"[].fields[\\\"time.idle\\\"]\" => \"[duration]\",\n        \"[].span.trace_id\" => insta::dynamic_redaction(move |value, _path| {\n            assert!(let Some(tracing_trace_id) = value.as_str());\n            check!(trace_id_1 == tracing_trace_id);\n            if is_trace_id_constant {\n                tracing_trace_id.to_string()\n            } else {\n                format!(\"[trace_id:lg{}]\", tracing_trace_id.len())\n            }\n        }),\n        \"[].spans[].trace_id\" => insta::dynamic_redaction(move |value, _path| {\n            assert!(let Some(tracing_trace_id) = value.as_str());\n            check!(trace_id_2 == tracing_trace_id);\n            if is_trace_id_constant {\n                tracing_trace_id.to_string()\n            } else {\n                format!(\"[trace_id:lg{}]\", tracing_trace_id.len())\n            }\n        }),\n    });\n    insta::assert_yaml_snapshot!(format!(\"{}_otel_spans\", name), otel_spans, {\n        \"[].start_time_unix_nano\" => \"[timestamp]\",\n        \"[].end_time_unix_nano\" => \"[timestamp]\",\n        \"[].events[].time_unix_nano\" => \"[timestamp]\",\n        \"[].trace_id\" => insta::dynamic_redaction(move |value, _path| {\n            assert!(let Some(otel_trace_id) = value.as_str());\n            //FIXME check!(trace_id_3 == otel_trace_id);\n            format!(\"[trace_id:lg{}]\", otel_trace_id.len())\n        }),\n        \"[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].parent_span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].links[].trace_id\" => insta::dynamic_redaction(|value, _path| {\n            assert!(let Some(otel_trace_id) = value.as_str());\n            format!(\"[trace_id:lg{}]\", otel_trace_id.len())\n        }),\n        \"[].links[].span_id\" => insta::dynamic_redaction(|value, _path| {\n            assert!(let Some(span_id) = value.as_str());\n            format!(\"[span_id:lg{}]\", span_id.len())\n        }),\n        \"[].attributes.busy_ns\" => \"ignore\",\n        \"[].attributes.idle_ns\" => \"ignore\",\n        \"[].attributes.trace_id\" => \"ignore\",\n        \"[].attributes[\\\"code.lineno\\\"]\" => \"ignore\",\n        \"[].attributes[\\\"code.filepath\\\"]\" => \"ignore\",\n        \"[].attributes[\\\"thread.id\\\"]\" => \"ignore\",\n    });\n}\n\npub struct FakeEnvironment {\n    fake_collector: fake_opentelemetry_collector::FakeCollectorServer,\n    rx: Receiver<Vec<u8>>,\n    _subsciber_guard: tracing::subscriber::DefaultGuard,\n    tracer_provider: opentelemetry_sdk::trace::SdkTracerProvider,\n}\n\nimpl FakeEnvironment {\n    pub async fn setup() -> Self {\n        //use axum::body::HttpBody as _;\n        //use tower::{Service, ServiceExt};\n        use tracing_subscriber::layer::SubscriberExt;\n\n        // setup a non Noop OpenTelemetry tracer to have non-empty trace_id\n        let fake_collector = fake_opentelemetry_collector::FakeCollectorServer::start()\n            .await\n            .unwrap();\n        let tracer_provider =\n            fake_opentelemetry_collector::setup_tracer_provider(&fake_collector).await;\n        //let (tracer, mut req_rx) = fake_opentelemetry_collector::setup_tracer().await;\n        opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());\n        let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer_provider.tracer(\"fake\"));\n\n        let (make_writer, rx) = duplex_writer();\n        let fmt_layer = tracing_subscriber::fmt::layer()\n            .json()\n            .with_writer(make_writer)\n            .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE);\n        let subscriber = tracing_subscriber::registry()\n            .with(EnvFilter::try_new(\"trace\").unwrap())\n            .with(fmt_layer)\n            .with(otel_layer);\n        let _subsciber_guard = subscriber.set_default();\n        Self {\n            fake_collector,\n            rx,\n            _subsciber_guard,\n            tracer_provider,\n        }\n    }\n\n    pub async fn collect_traces(\n        &mut self,\n    ) -> (Vec<Value>, Vec<fake_opentelemetry_collector::ExportedSpan>) {\n        let _ = self.tracer_provider.force_flush();\n\n        let otel_spans = self\n            .fake_collector\n            .exported_spans(1, std::time::Duration::from_millis(100))\n            .await;\n        // insta::assert_debug_snapshot!(first_span);\n        let tracing_events = std::iter::from_fn(|| {\n            self.rx\n                .recv_timeout(std::time::Duration::from_millis(3))\n                //.recv()\n                .ok()\n        })\n        .map(|bytes| serde_json::from_slice::<Value>(&bytes).unwrap())\n        .collect::<Vec<_>>();\n        (tracing_events, otel_spans)\n    }\n}\n\nfn duplex_writer() -> (DuplexWriter, Receiver<Vec<u8>>) {\n    let (tx, rx) = mpsc::sync_channel(1024);\n    (DuplexWriter { tx }, rx)\n}\n\n#[derive(Clone)]\nstruct DuplexWriter {\n    tx: SyncSender<Vec<u8>>,\n}\n\nimpl<'a> MakeWriter<'a> for DuplexWriter {\n    type Writer = Self;\n\n    fn make_writer(&'a self) -> Self::Writer {\n        self.clone()\n    }\n}\n\nimpl std::io::Write for DuplexWriter {\n    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        self.tx.send(buf.to_vec()).unwrap();\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__call_with_w3c_trace.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\nsnapshot_kind: text\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: \"/users/{id}\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: \"GET /users/{id}\"\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__call_with_w3c_trace_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg16]\"\n  name: \"GET /users/{id}\"\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/users/{id}\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_6\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/users/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__empty_http_route_for_nonexisting_route.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /idontexist/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 404\n    http.route: \"\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /idontexist/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__empty_http_route_for_nonexisting_route_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: GET\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"404\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_2\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/idontexist/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__extract_route_from_nested.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\nsnapshot_kind: text\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /nest/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: \"/nest/{nest_id}\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: \"GET /nest/{nest_id}\"\n    server.address: \"\"\n    span.type: web\n    url.path: /nest/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__extract_route_from_nested_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: \"GET /nest/{nest_id}\"\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/nest/{nest_id}\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_9\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/nest/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__filled_http_headers.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\nsnapshot_kind: text\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: tests\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: \"/users/{id}\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: \"GET /users/{id}\"\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: tests\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__filled_http_headers_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: \"GET /users/{id}\"\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/users/{id}\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_5\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/users/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"tests\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__filled_http_route_for_existing_route.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\nsnapshot_kind: text\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: example.com\n    span.type: web\n    url.path: /users/123\n    url.scheme: http\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: \"/users/{id}\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: \"GET /users/{id}\"\n    server.address: example.com\n    span.type: web\n    url.path: /users/123\n    url.scheme: http\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__filled_http_route_for_existing_route_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: \"GET /users/{id}\"\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/users/{id}\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"example.com\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_1\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/users/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"http\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__status_code_on_close_for_error.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /status/500\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 500\n    http.route: /status/500\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET /status/500\n    otel.status_code: ERROR\n    server.address: \"\"\n    span.type: web\n    url.path: /status/500\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__status_code_on_close_for_error_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: GET /status/500\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"500\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/status/500\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_4\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/status/500\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_ERROR\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__status_code_on_close_for_ok.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\nsnapshot_kind: text\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: \"/users/{id}\"\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: \"GET /users/{id}\"\n    server.address: \"\"\n    span.type: web\n    url.path: /users/123\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__status_code_on_close_for_ok_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: \"GET /users/{id}\"\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/users/{id}\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_3\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/users/123\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__trace_id_in_child_span.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /with_child_span\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: new\n  level: INFO\n  span:\n    name: my child span\n  spans:\n    - http.request.method: GET\n      http.route: /with_child_span\n      name: HTTP request\n      network.protocol.version: \"1.1\"\n      otel.kind: Server\n      otel.name: GET /with_child_span\n      server.address: \"\"\n      span.type: web\n      url.path: /with_child_span\n      url.scheme: \"\"\n      user_agent.original: \"\"\n  target: \"axum_tracing_opentelemetry::middleware::trace_extractor::tests\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: INFO\n  span:\n    name: my child span\n  spans:\n    - http.request.method: GET\n      http.route: /with_child_span\n      name: HTTP request\n      network.protocol.version: \"1.1\"\n      otel.kind: Server\n      otel.name: GET /with_child_span\n      server.address: \"\"\n      span.type: web\n      url.path: /with_child_span\n      url.scheme: \"\"\n      user_agent.original: \"\"\n  target: \"axum_tracing_opentelemetry::middleware::trace_extractor::tests\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: /with_child_span\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET /with_child_span\n    server.address: \"\"\n    span.type: web\n    url.path: /with_child_span\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__trace_id_in_child_span_for_remote.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: tracing_events\n---\n- fields:\n    message: new\n  level: TRACE\n  span:\n    http.request.method: GET\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET\n    server.address: \"\"\n    span.type: web\n    url.path: /with_child_span\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: new\n  level: INFO\n  span:\n    name: my child span\n  spans:\n    - http.request.method: GET\n      http.route: /with_child_span\n      name: HTTP request\n      network.protocol.version: \"1.1\"\n      otel.kind: Server\n      otel.name: GET /with_child_span\n      server.address: \"\"\n      span.type: web\n      url.path: /with_child_span\n      url.scheme: \"\"\n      user_agent.original: \"\"\n  target: \"axum_tracing_opentelemetry::middleware::trace_extractor::tests\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: INFO\n  span:\n    name: my child span\n  spans:\n    - http.request.method: GET\n      http.route: /with_child_span\n      name: HTTP request\n      network.protocol.version: \"1.1\"\n      otel.kind: Server\n      otel.name: GET /with_child_span\n      server.address: \"\"\n      span.type: web\n      url.path: /with_child_span\n      url.scheme: \"\"\n      user_agent.original: \"\"\n  target: \"axum_tracing_opentelemetry::middleware::trace_extractor::tests\"\n  timestamp: \"[timestamp]\"\n- fields:\n    message: close\n    time.busy: \"[duration]\"\n    time.idle: \"[duration]\"\n  level: TRACE\n  span:\n    http.request.method: GET\n    http.response.status_code: 200\n    http.route: /with_child_span\n    name: HTTP request\n    network.protocol.version: \"1.1\"\n    otel.kind: Server\n    otel.name: GET /with_child_span\n    server.address: \"\"\n    span.type: web\n    url.path: /with_child_span\n    url.scheme: \"\"\n    user_agent.original: \"\"\n  spans: []\n  target: \"otel::tracing\"\n  timestamp: \"[timestamp]\"\n\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__trace_id_in_child_span_for_remote_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg16]\"\n  name: my child span\n  kind: SPAN_KIND_INTERNAL\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"axum-tracing-opentelemetry/src/middleware/trace_extractor.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(257)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"axum_tracing_opentelemetry::middleware::trace_extractor::tests\\\")) })\"\n    idle_ns: ignore\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"axum_tracing_opentelemetry::middleware::trace_extractor::tests\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_8\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg16]\"\n  name: GET /with_child_span\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/with_child_span\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_8\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/with_child_span\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "testing-tracing-opentelemetry/src/snapshots/testing_tracing_opentelemetry__trace_id_in_child_span_otel_spans.snap",
    "content": "---\nsource: testing-tracing-opentelemetry/src/lib.rs\nexpression: otel_spans\n---\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg16]\"\n  name: my child span\n  kind: SPAN_KIND_INTERNAL\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"axum-tracing-opentelemetry/src/middleware/trace_extractor.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(257)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"axum_tracing_opentelemetry::middleware::trace_extractor::tests\\\")) })\"\n    idle_ns: ignore\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"axum_tracing_opentelemetry::middleware::trace_extractor::tests\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_7\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n- trace_id: \"[trace_id:lg32]\"\n  span_id: \"[span_id:lg16]\"\n  trace_state: \"\"\n  parent_span_id: \"[span_id:lg0]\"\n  name: GET /with_child_span\n  kind: SPAN_KIND_SERVER\n  start_time_unix_nano: \"[timestamp]\"\n  end_time_unix_nano: \"[timestamp]\"\n  attributes:\n    busy_ns: ignore\n    code.file.path: \"Some(AnyValue { value: Some(StringValue(\\\"tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs\\\")) })\"\n    code.line.number: \"Some(AnyValue { value: Some(IntValue(15)) })\"\n    code.module.name: \"Some(AnyValue { value: Some(StringValue(\\\"tracing_opentelemetry_instrumentation_sdk::http::http_server\\\")) })\"\n    http.request.method: \"Some(AnyValue { value: Some(StringValue(\\\"GET\\\")) })\"\n    http.response.status_code: \"Some(AnyValue { value: Some(StringValue(\\\"200\\\")) })\"\n    http.route: \"Some(AnyValue { value: Some(StringValue(\\\"/with_child_span\\\")) })\"\n    idle_ns: ignore\n    network.protocol.version: \"Some(AnyValue { value: Some(StringValue(\\\"1.1\\\")) })\"\n    server.address: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    span.type: \"Some(AnyValue { value: Some(StringValue(\\\"web\\\")) })\"\n    target: \"Some(AnyValue { value: Some(StringValue(\\\"otel::tracing\\\")) })\"\n    thread.id: ignore\n    thread.name: \"Some(AnyValue { value: Some(StringValue(\\\"middleware::trace_extractor::tests::check_span_event::case_7\\\")) })\"\n    url.path: \"Some(AnyValue { value: Some(StringValue(\\\"/with_child_span\\\")) })\"\n    url.scheme: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n    user_agent.original: \"Some(AnyValue { value: Some(StringValue(\\\"\\\")) })\"\n  dropped_attributes_count: 0\n  events: []\n  dropped_events_count: 0\n  links: []\n  dropped_links_count: 0\n  status:\n    message: \"\"\n    code: STATUS_CODE_UNSET\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.30.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.29.1...tonic-tracing-opentelemetry-v0.30.0) - 2025-09-27\n\n### <!-- 2 -->Added\n\n- [**breaking**] export grpc utils from `http::grpc` module\n\n## [0.29.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.28.1...tonic-tracing-opentelemetry-v0.29.0) - 2025-06-03\n\n### <!-- 2 -->Added\n\n- *(deps)* update opentelemetry 0.30 & tonic 0.13 (#240)\n\n## [0.26.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.26.0...tonic-tracing-opentelemetry-v0.26.1) - 2025-02-26\n\n### <!-- 3 -->Removed\n\n- *(deps)* remove minor constraint when major > 1\n\n## [0.24.3](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.24.2...tonic-tracing-opentelemetry-v0.24.3) - 2025-01-07\n\n### <!-- 1 -->Fixed\n\n- Implement tower::Service for OtelGrpcService (#201)\n\n## [0.21.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.19.0...tonic-tracing-opentelemetry-v0.21.0) - 2024-08-31\n\n### <!-- 4 -->Changed\n- 💄 update deprecated syntax \"default_features\" in Cargo.toml\n- ⬆️ upgrade to tonic 0.12\n- ⬆️ upgrade to rstest 0.22\n\n## [0.18.2](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tonic-tracing-opentelemetry-v0.18.1...tonic-tracing-opentelemetry-v0.18.2) - 2024-04-24\n\n### <!-- 2 -->Added\n- ✨ allow to create span for opentelemetry at level `info` with feature flag `tracing_level_info`\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/Cargo.toml",
    "content": "[package]\nname = \"tonic-tracing-opentelemetry\"\ndescription = \"Middlewares and tools to integrate tonic + tracing + opentelemetry.\"\nreadme = \"README.md\"\nkeywords = [\"tonic\", \"tracing\", \"opentelemetry\"]\ncategories = [\n  \"development-tools::debugging\",\n  \"development-tools::profiling\",\n  \"web-programming\",\n]\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/tonic-tracing-opentelemetry\"\nedition.workspace = true\nversion = \"0.32.2\"\nrepository.workspace = true\nlicense.workspace = true\n\n[dependencies]\nfutures-core = \"0.3\"\nfutures-util = { version = \"0.3\", default-features = false, features = [] }\nhttp = { workspace = true }\nhttp-body = \"1\"\nhyper = { workspace = true }\nopentelemetry = { workspace = true }\npin-project-lite = \"0.2\"\ntonic = { workspace = true, default-features = false }\ntower = { workspace = true }\ntracing = { workspace = true }\ntracing-opentelemetry = { workspace = true }\ntracing-opentelemetry-instrumentation-sdk = { path = \"../tracing-opentelemetry-instrumentation-sdk\", features = [\n  \"http\",\n], version = \"0.32\" }\n\n[dev-dependencies]\naxum = { workspace = true }\ntesting-tracing-opentelemetry = { path = \"../testing-tracing-opentelemetry\" }\nfake-opentelemetry-collector = { path = \"../fake-opentelemetry-collector\" }\nassert2 = { workspace = true }\ninsta = { workspace = true }\nopentelemetry-otlp = { workspace = true, features = [\n  \"http-proto\",\n  \"reqwest-client\",\n  \"reqwest-rustls\",\n] }\nopentelemetry-proto = { workspace = true, features = [\"gen-tonic\"] }\nrstest = { workspace = true }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\ntokio = { workspace = true, features = [\"full\"] }\ntracing-subscriber = { version = \"0.3\", default-features = false, features = [\n  \"env-filter\",\n  \"fmt\",\n  \"json\",\n] }\ntokio-stream = { workspace = true, features = [\"net\"] }\n# need tokio runtime to run smoke tests.\nopentelemetry_sdk = { workspace = true, features = [\n  \"trace\",\n  \"rt-tokio\",\n  \"testing\",\n] }\n\n[features]\ndefault = []\n# to use level `info` instead of `trace` to create otel span\ntracing_level_info = [\n  \"tracing-opentelemetry-instrumentation-sdk/tracing_level_info\",\n]\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/README.md",
    "content": "# tonic-tracing-opentelemetry\n\n[![crates license](https://img.shields.io/crates/l/tonic-tracing-opentelemetry.svg)](http://creativecommons.org/publicdomain/zero/1.0/)\n[![crate version](https://img.shields.io/crates/v/tonic-tracing-opentelemetry.svg)](https://crates.io/crates/tonic-tracing-opentelemetry)\n\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n\nMiddlewares and tools to integrate tonic + tracing + opentelemetry for client and server.\n\n> Really early, missing lot of features, help is welcomed.\n\n- Read OpenTelemetry header from the incoming requests\n- Start a new trace if no trace is found in the incoming request\n- Trace is attached into tracing's span\n\nFor examples, you can look at the [examples](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/examples/) folder.\n\nExtract of `client.rs`:\n\n```txt\n    let channel = Channel::from_static(\"http://127.0.0.1:50051\")\n        .connect()\n        .await?; //Devskim: ignore DS137138\n    let channel = ServiceBuilder::new()\n        .layer(OtelGrpcLayer::default())\n        .service(channel);\n\n    let mut client = GreeterClient::new(channel);\n\n    //...\n\n    opentelemetry::global::shutdown_tracer_provider();\n```\n\nExtract of `server.rs`:\n\n```txt\n    Server::builder()\n        // create trace for every request including health_service\n        .layer(server::OtelGrpcLayer::default().filter(filters::reject_healthcheck))\n        .add_service(health_service)\n        .add_service(reflection_service)\n        //.add_service(GreeterServer::new(greeter))\n        .add_service(GreeterServer::new(greeter))\n        .serve_with_shutdown(addr, shutdown_signal())\n        .await?;\n```\n\n## TODO\n\n- add test\n- add documentation\n- add examples\n- validate with [[opentelemetry-specification/rpc.md at main · open-telemetry/opentelemetry-specification · GitHub](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md#grpc)]\n\n## Changelog - History\n\n[CHANGELOG.md](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/main/CHANGELOG.md)\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/src/lib.rs",
    "content": "//#![warn(missing_docs)]\n#![forbid(unsafe_code)]\n#![warn(clippy::perf)]\n#![warn(clippy::pedantic)]\n#![allow(clippy::module_name_repetitions)]\n#![doc = include_str!(\"../README.md\")]\n\npub mod middleware;\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/src/middleware/client.rs",
    "content": "//! code based on [tonic/examples/src/tower/client.rs at master · hyperium/tonic · GitHub](https://github.com/hyperium/tonic/blob/master/examples/src/tower/client.rs)\nuse http::{Request, Response};\nuse pin_project_lite::pin_project;\nuse std::{\n    error::Error,\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tonic::client::GrpcService;\nuse tower::{Layer, Service};\nuse tracing::Span;\nuse tracing_opentelemetry_instrumentation_sdk::{find_context_from_tracing, http as otel_http};\n\n/// layer for grpc (tonic client):\n///\n/// - propagate `OpenTelemetry` context (`trace_id`,...) to server\n/// - create a Span for `OpenTelemetry` (and tracing) on call\n///\n/// `OpenTelemetry` context are extracted frim tracing's span.\n#[derive(Default, Debug, Clone)]\npub struct OtelGrpcLayer;\n\nimpl<S> Layer<S> for OtelGrpcLayer {\n    /// The wrapped service\n    type Service = OtelGrpcService<S>;\n    fn layer(&self, inner: S) -> Self::Service {\n        OtelGrpcService { inner }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct OtelGrpcService<S> {\n    inner: S,\n}\n\nimpl<S, B, B2> Service<Request<B>> for OtelGrpcService<S>\nwhere\n    S: GrpcService<B, ResponseBody = B2> + Clone + Send + 'static,\n    S::Future: Send + 'static,\n    S::Error: Error + 'static,\n    B: Send + 'static,\n    // B2: tonic::codegen::Body,\n    B2: http_body::Body,\n{\n    type Response = Response<B2>;\n    type Error = S::Error;\n    type Future = ResponseFuture<S::Future>;\n    // #[allow(clippy::type_complexity)]\n    // type Future =\n    //     futures::future::BoxFuture<'static, Result<http::Response<S::ResponseBody>, Self::Error>>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx) //.map_err(|e| e.into())\n    }\n\n    fn call(&mut self, req: Request<B>) -> Self::Future {\n        // This is necessary because tonic internally uses `tower::buffer::Buffer`.\n        // See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149\n        // for details on why this is necessary\n        // let clone = self.inner.clone();\n        // let mut inner = std::mem::replace(&mut self.inner, clone);\n        let mut req = req;\n        let span = otel_http::grpc_client::make_span_from_request(&req);\n        otel_http::inject_context(&find_context_from_tracing(&span), req.headers_mut());\n        let future = {\n            let _enter = span.enter();\n            self.inner.call(req)\n        };\n        ResponseFuture {\n            inner: future,\n            span,\n        }\n    }\n}\n\npin_project! {\n    /// Response future for [`Trace`].\n    ///\n    /// [`Trace`]: super::Trace\n    pub struct ResponseFuture<F> {\n        #[pin]\n        pub(crate) inner: F,\n        pub(crate) span: Span,\n        // pub(crate) start: Instant,\n    }\n}\n\nimpl<Fut, ResBody, E> Future for ResponseFuture<Fut>\nwhere\n    Fut: Future<Output = Result<Response<ResBody>, E>>,\n    E: std::error::Error + 'static,\n{\n    type Output = Result<Response<ResBody>, E>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let _guard = this.span.enter();\n        let result = futures_util::ready!(this.inner.poll(cx));\n        otel_http::grpc::update_span_from_response_or_error(this.span, &result);\n        Poll::Ready(result)\n    }\n}\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/src/middleware/filters.rs",
    "content": "#[must_use]\npub fn reject_healthcheck(path: &str) -> bool {\n    !path.contains(\"grpc.health.\") //\"grpc.health.v1.Health\"\n}\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/src/middleware/mod.rs",
    "content": "pub mod client;\npub mod filters;\npub mod server;\n"
  },
  {
    "path": "tonic-tracing-opentelemetry/src/middleware/server.rs",
    "content": "//! code based on [tonic/examples/src/tower/client.rs at master · hyperium/tonic · GitHub](https://github.com/hyperium/tonic/blob/master/examples/src/tower/client.rs)\nuse http::{Request, Response};\nuse pin_project_lite::pin_project;\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tower::{Layer, Service};\nuse tracing::Span;\nuse tracing_opentelemetry_instrumentation_sdk::http as otel_http;\n\npub type Filter = fn(&str) -> bool;\n\n/// layer for grpc (tonic client):\n///\n/// - propagate `OpenTelemetry` context (`trace_id`, ...) to server\n/// - create a Span for `OpenTelemetry` (and tracing) on call\n///\n/// `OpenTelemetry` context are extracted frim tracing's span.\n#[derive(Default, Debug, Clone)]\npub struct OtelGrpcLayer {\n    filter: Option<Filter>,\n}\n\n// add a builder like api\nimpl OtelGrpcLayer {\n    #[must_use]\n    pub fn filter(self, filter: Filter) -> Self {\n        OtelGrpcLayer {\n            filter: Some(filter),\n        }\n    }\n}\n\nimpl<S> Layer<S> for OtelGrpcLayer {\n    /// The wrapped service\n    type Service = OtelGrpcService<S>;\n    fn layer(&self, inner: S) -> Self::Service {\n        OtelGrpcService {\n            inner,\n            filter: self.filter,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct OtelGrpcService<S> {\n    inner: S,\n    filter: Option<Filter>,\n}\n\nimpl<S, B, B2> Service<Request<B>> for OtelGrpcService<S>\nwhere\n    S: Service<Request<B>, Response = Response<B2>> + Clone + Send + 'static,\n    //S::Future: Send + 'static,\n    S::Error: std::error::Error,\n    B: Send + 'static,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    type Future = ResponseFuture<S::Future>;\n    // #[allow(clippy::type_complexity)]\n    // type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;\n    //type Future = futures_core::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;\n    //type Future = Pin<Box<S::Future>>;\n    // type Future = S::Future;\n    //type Future = Inspect<S::Future, Box<dyn FnOnce(S::Response)>>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx)\n    }\n\n    fn call(&mut self, req: Request<B>) -> Self::Future {\n        use tracing_opentelemetry::OpenTelemetrySpanExt;\n        // This is necessary because tonic internally uses `tower::buffer::Buffer`.\n        // See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149\n        // for details on why this is necessary\n        // let clone = self.inner.clone();\n        // let mut inner = std::mem::replace(&mut self.inner, clone);\n        let req = req;\n        let span = if self.filter.is_none_or(|f| f(req.uri().path())) {\n            let span = otel_http::grpc_server::make_span_from_request(&req);\n            if let Err(error) = span.set_parent(otel_http::extract_context(req.headers())) {\n                tracing::warn!(?error, \"can not set parent trace_id to span\");\n            }\n            span\n        } else {\n            tracing::Span::none()\n        };\n        let future = {\n            let _enter = span.enter();\n            self.inner.call(req)\n        };\n        ResponseFuture {\n            inner: future,\n            span,\n        }\n    }\n}\n\npin_project! {\n    /// Response future for [`Trace`].\n    ///\n    /// [`Trace`]: super::Trace\n    pub struct ResponseFuture<F> {\n        #[pin]\n        pub(crate) inner: F,\n        pub(crate) span: Span,\n        // pub(crate) start: Instant,\n    }\n}\n\nimpl<Fut, ResBody, Error> Future for ResponseFuture<Fut>\nwhere\n    Fut: Future<Output = Result<Response<ResBody>, Error>>,\n    // Require that the inner service's error can be converted into a `BoxError`.\n    //Error: Into<BoxError>,\n    Error: std::error::Error,\n{\n    type Output = Result<Response<ResBody>, Error>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let _guard = this.span.enter();\n        let result = futures_util::ready!(this.inner.poll(cx));\n        otel_http::grpc::update_span_from_response_or_error(this.span, &result);\n        Poll::Ready(result)\n    }\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.30.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.29.1...tracing-opentelemetry-instrumentation-sdk-v0.30.0) - 2025-08-25\n\n### <!-- 2 -->Added\n\n- *(axum)* optional extraction of `client.address` (former `client_ip`) from http headers or socket's info\n# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.32.4](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.32.3...tracing-opentelemetry-instrumentation-sdk-v0.32.4) - 2026-03-15\n\n### <!-- 1 -->Fixed\n\n- *(deps)* MSRV bump rust to 1.91\n\n## [0.32.4](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.32.3...tracing-opentelemetry-instrumentation-sdk-v0.32.4) - 2026-03-15\n\n### <!-- 1 -->Fixed\n\n- *(deps)* MSRV bump rust to 1.91\n\n## [0.32.2](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.32.1...tracing-opentelemetry-instrumentation-sdk-v0.32.2) - 2025-11-13\n\n### <!-- 2 -->Added\n\n- add in features to docs.rs rendered content. ([#287](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/pull/287))\n\n## [0.32.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.32.0...tracing-opentelemetry-instrumentation-sdk-v0.32.1) - 2025-10-14\n\n### Wip\n\n- use `opentelemetry-semantic-conventions` instead of `static &str`\n\n## [0.31.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.30.0...tracing-opentelemetry-instrumentation-sdk-v0.31.0) - 2025-09-27\n\n### <!-- 2 -->Added\n\n- [**breaking**] export grpc utils from `http::grpc` module\n\n## [0.29.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.28.1...tracing-opentelemetry-instrumentation-sdk-v0.29.0) - 2025-06-03\n\n### <!-- 2 -->Added\n\n- *(deps)* update opentelemetry 0.30 & tonic 0.13 (#240)\n\n## [0.24.0](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.19.0...tracing-opentelemetry-instrumentation-sdk-v0.24.0) - 2024-08-31\n\n### <!-- 4 -->Changed\n- ⬆️ upgrade to tonic 0.12\n- ⬆️ upgrade to rstest 0.22\n\n## [0.18.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.18.0...tracing-opentelemetry-instrumentation-sdk-v0.18.1) - 2024-04-24\n\n### <!-- 2 -->Added\n- ✨ allow to create span for opentelemetry at level `info` with feature flag `tracing_level_info`\n\n## [0.17.1](https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/compare/tracing-opentelemetry-instrumentation-sdk-v0.17.0...tracing-opentelemetry-instrumentation-sdk-v0.17.1) - 2024-02-24\n\n### Other\n- 👷 tune release-plz\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/Cargo.toml",
    "content": "[package]\nname = \"tracing-opentelemetry-instrumentation-sdk\"\ndescription = \"A set of helpers to build OpenTelemetry instrumentation based on `tracing` crate.\"\nreadme = \"README.md\"\nkeywords = [\"tracing\", \"opentelemetry\"]\ncategories = [\n  \"development-tools::debugging\",\n  \"development-tools::profiling\",\n  \"web-programming\",\n]\nhomepage = \"https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/tree/main/tracing-opentelemetry-instrumentation-sdk\"\nedition.workspace = true\nversion = \"0.32.5\"\nrepository.workspace = true\nlicense.workspace = true\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docs_rs\"]\n\n[dependencies]\nhttp = { workspace = true, optional = true }\nopentelemetry = { workspace = true }\nopentelemetry-semantic-conventions = { workspace = true, features = [\n  \"semconv_experimental\",\n] }\ntracing = { workspace = true }\ntracing-opentelemetry = { workspace = true }\n\n[dev-dependencies]\nassert2 = { workspace = true }\nrstest = { workspace = true }\n\n[features]\ndefault = []\nhttp = [\"dep:http\"]\n# to use level `info` instead of `trace` to create otel span\ntracing_level_info = []\n\n[lints]\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(docs_rs)'] }\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/README.md",
    "content": "# tracing-opentelemetry-instrumentation-sdk\n\nProvide a set of helpers to build [OpenTelemetry] instrumentation based on [`tracing`] crate, and following the [OpenTelemetry Trace Semantic Conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.22.0/specification/trace/semantic_conventions).\n\nPS: Contributions are welcome (bug report, improvements, features, ...)\n\nInstrumentation on the caller side of a call is  composed of steps:\n\n- start a span with all the attributes (some set to `Empty`)\n- inject into the call (via header) the propagation data (if supported)\n- do the call\n- update attributes of the span with response (status,...)\n\nInstrumentation on the callee side of a call is  composed of steps:\n\n- extract info propagated info (from header) (if supported) an create an OpenTelemetry Context\n- start a span with all the attributes (some set to `Empty`)\n- attach the context as parent on the span\n- do the processing\n- update attributes of the span with response (status,...)\n\nThe crates provide helper (or inspiration) to extract/inject context info, start & update span and retrieve context or `trace_id` during processing (eg to inject `trace_id` into log, error message,...).\n\n```rust\n  let trace_id = tracing_opentelemetry_instrumentation_sdk::find_current_trace_id();\n  //json!({ \"error\" :  \"xxxxxx\", \"trace_id\": trace_id})\n```\n\nThe helpers could be used as is or into middleware build on it (eg: [`axum-tracing-opentelemetry`], [`tonic-tracing-opentelemetry`] are middlewares build on top of the helpers provide for `http` (feature & crate))\n\n## Notes\n\n- [`tracing-opentelemetry`] extends [`tracing`] to interoperate with [OpenTelemetry]. But with some constraints:\n  - Creation of the OpenTelemetry's span is done when the tracing span is closed. So do not try to interact with OpenTelemetry Span (or `SpanBuilder`) from inside the tracing span.\n  - The OpenTelemetry parent `Context` (and `trace_id`) is created on `NEW` span or inherited from parent span. The parent context can be overwritten after creation, but until then the `trace_id` is the one from `NEW`, So tracing's log could report none or not-yet set `trace_id` on event `NEW` and the following until update.\n  - To define kind, name,... of OpenTelemetry's span from tracing's span used special record's name: `otel.name`, `otel.kind`, ...\n  - Record in a [`tracing`]'s Span should be defined at creation time. So some field are created with value `tracing::field::Empty` to then being updated.\n- Create trace with target `otel::tracing` (and level `trace`), to have a common way to enable / to disable\n\n## Instrumentations Tips\n\nUntil every crates are instrumented\n\nUse `tracing::instrumented` (no propagation & no update on response)\n\n```txt\n// basic handmade span far to be compliant with\n//[opentelemetry-specification/.../database.md](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.22.0/specification/trace/semantic_conventions/database.md)\nfn make_otel_span(db_operation: &str) -> tracing::Span {\n    // NO parsing of statement to extract information, not recommended by Specification and time-consuming\n    // warning: providing the statement could leek information\n    tracing_opentelemetry_instrumentation_sdk::otel_trace_span!(\n        \"DB request\",\n        db.system = \"postgresql\",\n        // db.statement = stmt,\n        db.operation = db_operation,\n        otel.name = db_operation, // should be <db.operation> <db.name>.<db.sql.table>,\n        otel.kind = \"CLIENT\",\n        otel.status_code = tracing::field::Empty,\n    )\n}\n\n\n      // Insert or update\n        sqlx::query!(\n                \"INSERT INTO ...\",\n                id,\n                sub_key,\n                result,\n            )\n            .execute(&*self.pool)\n            .instrument(make_otel_span(\"INSERT\"))\n            .await\n            .map_err(...)?;\n```\n\n## Related crates\n\n- [`init-tracing-opentelemetry`] to initialize [`tracing`] & [OpenTelemetry]\n- [`axum-tracing-opentelemetry`] middlewares for axum based on [`tracing-opentelemetry-instrumentation-sdk`]\n- [`tonic-tracing-opentelemetry`] middlewares for tonic based on [`tracing-opentelemetry-instrumentation-sdk`]\n\n[`tracing-opentelemetry`]: https://crates.io/crates/tracing-opentelemetry\n[OpenTelemetry]: https://crates.io/crates/opentelemetry\n[`tracing`]: https://crates.io/crates/tracing\n[`axum-tracing-opentelemetry`]: https://crates.io/crates/axum-tracing-opentelemetry\n[`init-tracing-opentelemetry`]: https://crates.io/crates/init-tracing-opentelemetry\n[`tonic-tracing-opentelemetry`]: https://crates.io/crates/tonic-tracing-opentelemetry\n[`tracing-opentelemetry-instrumentation-sdk`]: https://crates.io/crates/tracing-opentelemetry-instrumentation-sdk\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/grpc.rs",
    "content": "use http::HeaderMap;\nuse opentelemetry_semantic_conventions::attribute::{\n    EXCEPTION_MESSAGE, OTEL_STATUS_CODE, RPC_GRPC_STATUS_CODE,\n};\n\n/// [`gRPC` status codes](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc)\n/// copied from tonic\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n#[allow(dead_code)]\npub enum GrpcCode {\n    /// The operation completed successfully.\n    Ok = 0,\n\n    /// The operation was cancelled.\n    Cancelled = 1,\n\n    /// Unknown error.\n    Unknown = 2,\n\n    /// Client specified an invalid argument.\n    InvalidArgument = 3,\n\n    /// Deadline expired before operation could complete.\n    DeadlineExceeded = 4,\n\n    /// Some requested entity was not found.\n    NotFound = 5,\n\n    /// Some entity that we attempted to create already exists.\n    AlreadyExists = 6,\n\n    /// The caller does not have permission to execute the specified operation.\n    PermissionDenied = 7,\n\n    /// Some resource has been exhausted.\n    ResourceExhausted = 8,\n\n    /// The system is not in a state required for the operation's execution.\n    FailedPrecondition = 9,\n\n    /// The operation was aborted.\n    Aborted = 10,\n\n    /// Operation was attempted past the valid range.\n    OutOfRange = 11,\n\n    /// Operation is not implemented or not supported.\n    Unimplemented = 12,\n\n    /// Internal error.\n    Internal = 13,\n\n    /// The service is currently unavailable.\n    Unavailable = 14,\n\n    /// Unrecoverable data loss or corruption.\n    DataLoss = 15,\n\n    /// The request does not have valid authentication credentials\n    Unauthenticated = 16,\n}\n\n/// If \"grpc-status\" can not be extracted from http response, the status \"0\" (Ok) is defined\n//TODO create similar but with tonic::Response<B> ? and use of [Status in tonic](https://docs.rs/tonic/latest/tonic/struct.Status.html) (more complete)\npub fn update_span_from_response<B>(\n    span: &tracing::Span,\n    response: &http::Response<B>,\n    is_spankind_server: bool,\n) {\n    let status = status_from_http_header(response.headers())\n        .or_else(|| status_from_http_status(response.status()))\n        .unwrap_or(GrpcCode::Ok as u16);\n    span.record(RPC_GRPC_STATUS_CODE, status);\n\n    if status_is_error(status, is_spankind_server) {\n        span.record(OTEL_STATUS_CODE, \"ERROR\");\n    } else {\n        span.record(OTEL_STATUS_CODE, \"OK\");\n    }\n}\n\n/// based on [Status in tonic](https://docs.rs/tonic/latest/tonic/struct.Status.html#method.from_header_map)\nfn status_from_http_header(headers: &HeaderMap) -> Option<u16> {\n    headers\n        .get(\"grpc-status\")\n        .and_then(|v| v.to_str().ok())\n        .and_then(|v| v.parse::<u16>().ok())\n}\n\nfn status_from_http_status(status_code: http::StatusCode) -> Option<u16> {\n    match status_code {\n        // Borrowed from https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md\n        http::StatusCode::BAD_REQUEST => Some(GrpcCode::Internal as u16),\n        http::StatusCode::UNAUTHORIZED => Some(GrpcCode::Unauthenticated as u16),\n        http::StatusCode::FORBIDDEN => Some(GrpcCode::PermissionDenied as u16),\n        http::StatusCode::NOT_FOUND => Some(GrpcCode::Unimplemented as u16),\n        http::StatusCode::TOO_MANY_REQUESTS\n        | http::StatusCode::BAD_GATEWAY\n        | http::StatusCode::SERVICE_UNAVAILABLE\n        | http::StatusCode::GATEWAY_TIMEOUT => Some(GrpcCode::Unavailable as u16),\n        // We got a 200 but no trailers, we can infer that this request is finished.\n        //\n        // This can happen when a streaming response sends two Status but\n        // gRPC requires that we end the stream after the first status.\n        //\n        // https://github.com/hyperium/tonic/issues/681\n        http::StatusCode::OK => None,\n        _ => Some(GrpcCode::Unknown as u16),\n    }\n}\n\n#[inline]\n#[must_use]\n/// see [Semantic Conventions for gRPC | OpenTelemetry](https://opentelemetry.io/docs/specs/semconv/rpc/grpc/)\n/// see [GRPC Core: Status codes and their use in gRPC](https://grpc.github.io/grpc/core/md_doc_statuscodes.html)\npub fn status_is_error(status: u16, is_spankind_server: bool) -> bool {\n    if is_spankind_server {\n        status == 2 || status == 4 || status == 12 || status == 13 || status == 14 || status == 15\n    } else {\n        status != 0\n    }\n}\n\nfn update_span_from_error<E>(span: &tracing::Span, error: &E)\nwhere\n    E: std::error::Error,\n{\n    span.record(OTEL_STATUS_CODE, \"ERROR\");\n    span.record(RPC_GRPC_STATUS_CODE, 2);\n    span.record(EXCEPTION_MESSAGE, error.to_string());\n    error\n        .source()\n        .map(|s| span.record(EXCEPTION_MESSAGE, s.to_string()));\n}\n\npub fn update_span_from_response_or_error<B, E>(\n    span: &tracing::Span,\n    response: &Result<http::Response<B>, E>,\n) where\n    E: std::error::Error,\n{\n    match response {\n        Ok(response) => {\n            update_span_from_response(span, response, true);\n        }\n        Err(err) => {\n            update_span_from_error(span, err);\n        }\n    }\n}\n\n// [opentelemetry-specification/.../rpc.md](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md)\n//TODO create similar but with tonic::Request<B> ?\n#[allow(clippy::needless_pass_by_value)]\npub(crate) fn make_span_from_request<B>(\n    req: &http::Request<B>,\n    kind: opentelemetry::trace::SpanKind,\n) -> tracing::Span {\n    use crate::http::{extract_service_method, http_host, user_agent};\n    use crate::otel_trace_span;\n    use tracing::field::Empty;\n\n    let (service, method) = extract_service_method(req.uri());\n    otel_trace_span!(\n        \"GRPC request\",\n        http.user_agent = %user_agent(req),\n        otel.name = format!(\"{service}/{method}\"),\n        otel.kind = ?kind,\n        otel.status_code = Empty,\n        rpc.system =\"grpc\",\n        rpc.service = %service,\n        rpc.method = %method,\n        rpc.grpc.status_code = Empty, // to set on response\n        server.address = %http_host(req),\n        exception.message = Empty, // to set on response\n        exception.details = Empty, // to set on response\n    )\n}\n\n// if let Some(host_name) = SYSTEM.host_name() {\n//     attributes.push(NET_HOST_NAME.string(host_name));\n// }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use rstest::rstest;\n\n    #[rstest]\n    #[case(0)]\n    #[case(16)]\n    #[case(-1)]\n    fn test_status_from_http_header(#[case] input: i32) {\n        let mut headers = http::HeaderMap::new();\n        headers.insert(\"grpc-status\", input.to_string().parse().unwrap());\n        if input > -1 {\n            assert_eq!(\n                status_from_http_header(&headers),\n                Some(u16::try_from(input).unwrap())\n            );\n        } else {\n            assert_eq!(status_from_http_header(&headers), None);\n        }\n    }\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/grpc_client.rs",
    "content": "use super::grpc;\n\npub fn make_span_from_request<B>(req: &http::Request<B>) -> tracing::Span {\n    grpc::make_span_from_request(req, opentelemetry::trace::SpanKind::Client)\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/grpc_server.rs",
    "content": "use super::grpc;\n\npub fn make_span_from_request<B>(req: &http::Request<B>) -> tracing::Span {\n    grpc::make_span_from_request(req, opentelemetry::trace::SpanKind::Server)\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/http_server.rs",
    "content": "use std::error::Error;\n\nuse crate::http::{http_flavor, http_host, url_scheme, user_agent};\nuse crate::otel_trace_span;\nuse crate::span_type::SpanType;\nuse opentelemetry_semantic_conventions::attribute::OTEL_STATUS_CODE;\nuse opentelemetry_semantic_conventions::trace::{EXCEPTION_MESSAGE, HTTP_RESPONSE_STATUS_CODE};\nuse tracing::field::Empty;\n\npub fn make_span_from_request<B>(req: &http::Request<B>) -> tracing::Span {\n    // [semantic-conventions/.../http-spans.md](https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/http/http-spans.md)\n    // [semantic-conventions/.../general/attributes.md](https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/general/attributes.md)\n    // Can not use const or opentelemetry_semantic_conventions::trace::* for name of records\n    let http_method = req.method();\n    otel_trace_span!(\n        \"HTTP request\",\n        http.request.method = %http_method,\n        http.route = Empty, // to set by router of \"webframework\" after\n        network.protocol.version = %http_flavor(req.version()),\n        server.address = http_host(req),\n        // server.port = req.uri().port(),\n        http.client.address = Empty, //%$request.connection_info().realip_remote_addr().unwrap_or(\"\"),\n        user_agent.original = user_agent(req),\n        http.response.status_code = Empty, // to set on response\n        url.path = req.uri().path(),\n        url.query = req.uri().query(),\n        url.scheme = url_scheme(req.uri()),\n        otel.name = %http_method, // to set by router of \"webframework\" after\n        otel.kind = ?opentelemetry::trace::SpanKind::Server,\n        otel.status_code = Empty, // to set on response\n        trace_id = Empty, // to set on response\n        request_id = Empty, // to set\n        exception.message = Empty, // to set on response\n        \"span.type\" = %SpanType::Web, // non-official open-telemetry key, only supported by Datadog\n    )\n}\n\npub fn update_span_from_response<B>(span: &tracing::Span, response: &http::Response<B>) {\n    let status = response.status();\n    span.record(HTTP_RESPONSE_STATUS_CODE, status.as_u16());\n\n    if status.is_server_error() {\n        span.record(OTEL_STATUS_CODE, \"ERROR\");\n        // see [http-spans.md#status](https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/http/http-spans.md#status)\n        // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges,\n        // unless there was another error (e.g., network error receiving the response body;\n        // or 3xx codes with max redirects exceeded), in which case status MUST be set to Error.\n        // } else {\n        //     span.record(OTEL_STATUS_CODE, \"OK\");\n    }\n}\n\npub fn update_span_from_error<E>(span: &tracing::Span, error: &E)\nwhere\n    E: Error,\n{\n    span.record(OTEL_STATUS_CODE, \"ERROR\");\n    //span.record(HTTP_RESPONSE_STATUS_CODE, 500);\n    span.record(EXCEPTION_MESSAGE, error.to_string());\n    error\n        .source()\n        .map(|s| span.record(EXCEPTION_MESSAGE, s.to_string()));\n}\n\npub fn update_span_from_response_or_error<B, E>(\n    span: &tracing::Span,\n    response: &Result<http::Response<B>, E>,\n) where\n    E: Error,\n{\n    match response {\n        Ok(response) => {\n            update_span_from_response(span, response);\n        }\n        Err(err) => {\n            update_span_from_error(span, err);\n        }\n    }\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/mod.rs",
    "content": "pub mod grpc;\npub mod grpc_client;\npub mod grpc_server;\npub mod http_server;\nmod opentelemetry_http;\n\nmod tools;\npub use tools::*;\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/opentelemetry_http.rs",
    "content": "use opentelemetry::propagation::{Extractor, Injector};\n\n// copy from crate opentelemetry-http (to not be dependants of on 3rd: http, ...)\npub struct HeaderInjector<'a>(pub &'a mut http::HeaderMap);\n\nimpl Injector for HeaderInjector<'_> {\n    /// Set a key and value in the `HeaderMap`. Does nothing if the key or value are not valid inputs.\n    fn set(&mut self, key: &str, value: String) {\n        if let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes())\n            && let Ok(val) = http::header::HeaderValue::from_str(&value)\n        {\n            self.0.insert(name, val);\n        }\n    }\n}\n\npub struct HeaderExtractor<'a>(pub &'a http::HeaderMap);\n\nimpl Extractor for HeaderExtractor<'_> {\n    /// Get a value for a key from the `HeaderMap`. If the value is not valid ASCII, returns None.\n    fn get(&self, key: &str) -> Option<&str> {\n        self.0.get(key).and_then(|value| value.to_str().ok())\n    }\n\n    /// Collect all the keys from the `HeaderMap`.\n    fn keys(&self) -> Vec<&str> {\n        self.0\n            .keys()\n            .map(http::HeaderName::as_str)\n            .collect::<Vec<_>>()\n    }\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/http/tools.rs",
    "content": "use std::borrow::Cow;\n\nuse http::{HeaderMap, Uri, Version};\nuse opentelemetry::Context;\n\nuse super::opentelemetry_http::{HeaderExtractor, HeaderInjector};\n\npub fn inject_context(context: &Context, headers: &mut http::HeaderMap) {\n    let mut injector = HeaderInjector(headers);\n    opentelemetry::global::get_text_map_propagator(|propagator| {\n        propagator.inject_context(context, &mut injector);\n    });\n}\n\n// If remote request has no span data the propagator defaults to an unsampled context\n#[must_use]\npub fn extract_context(headers: &http::HeaderMap) -> Context {\n    let extractor = HeaderExtractor(headers);\n    opentelemetry::global::get_text_map_propagator(|propagator| propagator.extract(&extractor))\n}\n\npub fn extract_service_method(uri: &Uri) -> (&str, &str) {\n    let path = uri.path();\n    let mut parts = path.split('/').filter(|x| !x.is_empty());\n    let service = parts.next().unwrap_or_default();\n    let method = parts.next().unwrap_or_default();\n    (service, method)\n}\n\n#[must_use]\n// From [X-Forwarded-For - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)\n// > If a request goes through multiple proxies, the IP addresses of each successive proxy is listed.\n// > This means that, given well-behaved client and proxies,\n// > the rightmost IP address is the IP address of the most recent proxy and\n// > the leftmost IP address is the IP address of the originating client.\npub fn extract_client_ip_from_headers(headers: &HeaderMap) -> Option<&str> {\n    extract_client_ip_from_forwarded(headers)\n        .or_else(|| extract_client_ip_from_x_forwarded_for(headers))\n}\n\n#[must_use]\n// From [X-Forwarded-For - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)\n// > If a request goes through multiple proxies, the IP addresses of each successive proxy is listed.\n// > This means that, given well-behaved client and proxies,\n// > the rightmost IP address is the IP address of the most recent proxy and\n// > the leftmost IP address is the IP address of the originating client.\nfn extract_client_ip_from_x_forwarded_for(headers: &HeaderMap) -> Option<&str> {\n    let value = headers.get(\"x-forwarded-for\")?;\n    let value = value.to_str().ok()?;\n    let mut ips = value.split(',');\n    Some(ips.next()?.trim())\n}\n\n#[must_use]\n// see [Forwarded header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Forwarded)\nfn extract_client_ip_from_forwarded(headers: &HeaderMap) -> Option<&str> {\n    let value = headers.get(\"forwarded\")?;\n    let value = value.to_str().ok()?;\n    value\n        .split(';')\n        .flat_map(|directive| directive.split(','))\n        // select the left/first \"for\" key\n        .find_map(|directive| directive.trim().strip_prefix(\"for=\"))\n        // ipv6 are enclosed into `[\"...\"]`\n        // string are enclosed into `\"...\"`\n        .map(|directive| {\n            directive\n                .trim_start_matches('[')\n                .trim_end_matches(']')\n                .trim_matches('\"')\n                .trim()\n        })\n}\n\n#[inline]\npub fn http_target(uri: &Uri) -> &str {\n    uri.path_and_query()\n        .map_or(\"\", http::uri::PathAndQuery::as_str)\n}\n\n#[inline]\n#[must_use]\npub fn http_flavor(version: Version) -> Cow<'static, str> {\n    match version {\n        Version::HTTP_09 => \"0.9\".into(),\n        Version::HTTP_10 => \"1.0\".into(),\n        Version::HTTP_11 => \"1.1\".into(),\n        Version::HTTP_2 => \"2.0\".into(),\n        Version::HTTP_3 => \"3.0\".into(),\n        other => format!(\"{other:?}\").into(),\n    }\n}\n\n#[inline]\npub fn url_scheme(uri: &Uri) -> &str {\n    uri.scheme_str().unwrap_or_default()\n}\n\n#[inline]\npub fn user_agent<B>(req: &http::Request<B>) -> &str {\n    req.headers()\n        .get(http::header::USER_AGENT)\n        .map_or(\"\", |h| h.to_str().unwrap_or(\"\"))\n}\n\n#[inline]\npub fn http_host<B>(req: &http::Request<B>) -> &str {\n    req.headers()\n        .get(http::header::HOST)\n        .map_or(req.uri().host(), |h| h.to_str().ok())\n        .unwrap_or(\"\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use assert2::assert;\n    use rstest::rstest;\n\n    #[rstest]\n    // #[case(\"\", \"\", \"\")]\n    #[case(\"/\", \"\", \"\")]\n    #[case(\"//\", \"\", \"\")]\n    #[case(\"/grpc.health.v1.Health/Check\", \"grpc.health.v1.Health\", \"Check\")]\n    fn test_extract_service_method(\n        #[case] path: &str,\n        #[case] service: &str,\n        #[case] method: &str,\n    ) {\n        assert!(extract_service_method(&path.parse::<Uri>().unwrap()) == (service, method));\n    }\n\n    #[rstest]\n    #[case(\"http://example.org/hello/world\", \"http\")] // Devskim: ignore DS137138\n    #[case(\"https://example.org/hello/world\", \"https\")]\n    #[case(\"foo://example.org/hello/world\", \"foo\")]\n    fn test_extract_url_scheme(#[case] input: &str, #[case] expected: &str) {\n        let uri: Uri = input.parse().unwrap();\n        assert!(url_scheme(&uri) == expected);\n    }\n\n    #[rstest]\n    #[case(\"\", \"\")]\n    #[case(\n        \"2001:db8:85a3:8d3:1319:8a2e:370:7348\",\n        \"2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n    )]\n    #[case(\"203.0.113.195\", \"203.0.113.195\")]\n    #[case(\"203.0.113.195,10.10.10.10\", \"203.0.113.195\")]\n    #[case(\"203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348\", \"203.0.113.195\")]\n    fn test_extract_client_ip_from_x_forwarded_for(#[case] input: &str, #[case] expected: &str) {\n        let mut headers = HeaderMap::new();\n        if !input.is_empty() {\n            headers.insert(\"X-Forwarded-For\", input.parse().unwrap());\n        }\n\n        let expected = if expected.is_empty() {\n            None\n        } else {\n            Some(expected)\n        };\n        assert!(extract_client_ip_from_x_forwarded_for(&headers) == expected);\n    }\n\n    #[rstest]\n    #[case(\"\", \"\")]\n    #[case(\n        \"for=[\\\"2001:db8:85a3:8d3:1319:8a2e:370:7348\\\"]\",\n        \"2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n    )]\n    #[case(\"for=203.0.113.195\", \"203.0.113.195\")]\n    #[case(\"for=203.0.113.195, for=10.10.10.10\", \"203.0.113.195\")]\n    #[case(\n        \"for=203.0.113.195, for=[\\\"2001:db8:85a3:8d3:1319:8a2e:370:7348\\\"]\",\n        \"203.0.113.195\"\n    )]\n    #[case(\"for=\\\"_mdn\\\"\", \"_mdn\")]\n    #[case(\"for=\\\"secret\\\"\", \"secret\")]\n    #[case(\"for=203.0.113.195;proto=http;by=203.0.113.43\", \"203.0.113.195\")]\n    #[case(\"proto=http;by=203.0.113.43\", \"\")]\n    fn test_extract_client_ip_from_forwarded(#[case] input: &str, #[case] expected: &str) {\n        let mut headers = HeaderMap::new();\n        if !input.is_empty() {\n            headers.insert(\"Forwarded\", input.parse().unwrap());\n        }\n\n        let expected = if expected.is_empty() {\n            None\n        } else {\n            Some(expected)\n        };\n        assert!(extract_client_ip_from_forwarded(&headers) == expected);\n    }\n}\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/lib.rs",
    "content": "//#![warn(missing_docs)]\n#![forbid(unsafe_code)]\n#![warn(clippy::perf)]\n#![warn(clippy::pedantic)]\n#![allow(clippy::module_name_repetitions)]\n#![doc = include_str!(\"../README.md\")]\n#![cfg_attr(docs_rs, feature(doc_cfg))]\n\n#[cfg(feature = \"http\")]\n#[cfg_attr(docs_rs, doc(cfg(feature = \"http\")))]\npub mod http;\nmod span_type;\n\nuse opentelemetry::Context;\n\n/// tracing's target used by instrumentation library to create span\npub const TRACING_TARGET: &str = \"otel::tracing\";\n\n#[cfg(not(feature = \"tracing_level_info\"))]\npub const TRACING_LEVEL: tracing::Level = tracing::Level::TRACE;\n\n#[cfg(feature = \"tracing_level_info\")]\npub const TRACING_LEVEL: tracing::Level = tracing::Level::INFO;\n\n// const SPAN_NAME_FIELD: &str = \"otel.name\";\n// const SPAN_KIND_FIELD: &str = \"otel.kind\";\n// const SPAN_STATUS_CODE_FIELD: &str = \"otel.status_code\";\n// const SPAN_STATUS_MESSAGE_FIELD: &str = \"otel.status_message\";\n\n// const FIELD_EXCEPTION_MESSAGE: &str = \"exception.message\";\n// const FIELD_EXCEPTION_STACKTRACE: &str = \"exception.stacktrace\";\n// const HTTP_TARGET: &str = opentelemetry_semantic_conventions::trace::HTTP_TARGET.as_str();\n\n/// Constructs a span for the target `TRACING_TARGET` with the level `TRACING_LEVEL`.\n///\n/// [Fields] and [attributes] are set using the same syntax as the [`tracing::span!`]\n/// macro.\n//TODO find a way to use opentelemetry_semantic_conventions::attribute::* as part of the field\n#[macro_export]\nmacro_rules! otel_trace_span {\n    (parent: $parent:expr, $name:expr, $($field:tt)*) => {\n        tracing::span!(\n            target: $crate::TRACING_TARGET,\n            parent: $parent,\n            $crate::TRACING_LEVEL,\n            $name,\n            $($field)*\n        )\n    };\n    (parent: $parent:expr, $name:expr) => {\n        $crate::otel_trace_span!(parent: $parent, $name,)\n    };\n    ($name:expr, $($field:tt)*) => {\n        tracing::span!(\n            target: $crate::TRACING_TARGET,\n            $crate::TRACING_LEVEL,\n            $name,\n            $($field)*\n        )\n    };\n    ($name:expr) => {\n        $crate::otel_trace_span!($name,)\n    };\n}\n\n#[inline]\n#[must_use]\npub fn find_current_context() -> Context {\n    use tracing_opentelemetry::OpenTelemetrySpanExt;\n    // let context = opentelemetry::Context::current();\n    // OpenTelemetry Context is propagation inside code is done via tracing crate\n    tracing::Span::current().context()\n}\n\n/// Search the current opentelemetry trace id into the Context from the current tracing'span.\n/// This function can be used to report the trace id into the error message send back to user.\n///\n/// ```rust\n/// let trace_id = tracing_opentelemetry_instrumentation_sdk::find_current_trace_id();\n/// // json!({ \"error\" :  \"xxxxxx\", \"trace_id\": trace_id})\n///\n/// ```\n#[inline]\n#[must_use]\npub fn find_current_trace_id() -> Option<String> {\n    find_trace_id(&find_current_context())\n}\n\n#[inline]\n#[must_use]\npub fn find_context_from_tracing(span: &tracing::Span) -> Context {\n    use tracing_opentelemetry::OpenTelemetrySpanExt;\n    // let context = opentelemetry::Context::current();\n    // OpenTelemetry Context is propagation inside code is done via tracing crate\n    span.context()\n}\n\n#[inline]\n#[must_use]\npub fn find_trace_id_from_tracing(span: &tracing::Span) -> Option<String> {\n    use tracing_opentelemetry::OpenTelemetrySpanExt;\n    // let context = opentelemetry::Context::current();\n    // OpenTelemetry Context is propagation inside code is done via tracing crate\n    find_trace_id(&span.context())\n}\n\n#[inline]\n#[must_use]\npub fn find_trace_id(context: &Context) -> Option<String> {\n    use opentelemetry::trace::TraceContextExt;\n\n    let span = context.span();\n    let span_context = span.span_context();\n    span_context\n        .is_valid()\n        .then(|| span_context.trace_id().to_string())\n\n    // #[cfg(not(any(\n    //     feature = \"opentelemetry_0_17\",\n    //     feature = \"opentelemetry_0_18\",\n    //     feature = \"opentelemetry_0_19\"\n    // )))]\n    // let trace_id = span.context().span().span_context().trace_id().to_hex();\n\n    // #[cfg(any(\n    //     feature = \"opentelemetry_0_17\",\n    //     feature = \"opentelemetry_0_18\",\n    //     feature = \"opentelemetry_0_19\"\n    // ))]\n    // let trace_id = {\n    //     let id = span.context().span().span_context().trace_id();\n    //     format!(\"{:032x}\", id)\n    // };\n}\n\n#[inline]\n#[must_use]\npub fn find_span_id(context: &Context) -> Option<String> {\n    use opentelemetry::trace::TraceContextExt;\n\n    let span = context.span();\n    let span_context = span.span_context();\n    span_context\n        .is_valid()\n        .then(|| span_context.span_id().to_string())\n}\n\n// pub(crate) fn set_otel_parent(parent_context: Context, span: &tracing::Span) {\n//     use opentelemetry::trace::TraceContextExt as _;\n//     use tracing_opentelemetry::OpenTelemetrySpanExt as _;\n\n//     // let parent_context = opentelemetry::global::get_text_map_propagator(|propagator| {\n//     //     propagator.extract(&RequestHeaderCarrier::new(req.headers()))\n//     // });\n//     span.set_parent(parent_context);\n//     // If we have a remote parent span, this will be the parent's trace identifier.\n//     // If not, it will be the newly generated trace identifier with this request as root span.\n\n//     if let Some(trace_id) = find_trace_id_from_tracing(&span) {\n//         span.record(\"trace_id\", trace_id);\n//     }\n// }\n\npub type BoxError = Box<dyn std::error::Error + Send + Sync>;\n"
  },
  {
    "path": "tracing-opentelemetry-instrumentation-sdk/src/span_type.rs",
    "content": "use std::fmt::Display;\n\n// SpanType is a non official open-telemetry key, only supported by Datadog, to help categorize traces.\n// Documentation: https://github.com/open-telemetry/opentelemetry-rust/blob/ccb510fbd6fdef9694e3b751fd01dbe33c7345c0/opentelemetry-datadog/src/lib.rs#L29-L30\n// Usage: It should be informed as span.type span key\n// Reference: https://github.com/DataDog/dd-trace-go/blob/352b090d4f90527d35a8ad535b97689e346589c8/ddtrace/ext/app_types.go#L31-L81\n#[allow(dead_code)]\npub enum SpanType {\n    Web,\n    Http,\n    Sql,\n    Cassandra,\n    Redis,\n    Memcached,\n    Mongodb,\n    Elasticsearch,\n    Leveldb,\n    Dns,\n    Queue,\n    Consul,\n    Graphql,\n}\n\nimpl Display for SpanType {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let s = match self {\n            SpanType::Web => \"web\",\n            SpanType::Http => \"http\",\n            SpanType::Sql => \"sql\",\n            SpanType::Cassandra => \"cassandra\",\n            SpanType::Redis => \"redis\",\n            SpanType::Memcached => \"memcached\",\n            SpanType::Mongodb => \"mongodb\",\n            SpanType::Elasticsearch => \"elasticsearch\",\n            SpanType::Leveldb => \"leveldb\",\n            SpanType::Dns => \"dns\",\n            SpanType::Queue => \"queue\",\n            SpanType::Consul => \"consul\",\n            SpanType::Graphql => \"graphql\",\n        };\n        f.write_str(s)\n    }\n}\n"
  }
]