├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── dotnet-format.yml │ ├── markdown-link-check.yml │ ├── markdownlint.yml │ ├── misspell.yml │ └── oats.yml ├── .gitignore ├── .markdown_link_check_config.json ├── .markdownlint.yaml ├── .vscode └── extensions.json ├── CHANGELOG.md ├── Directory.Build.props ├── GrafanaOpenTelemetry.sln ├── LICENSE ├── README.md ├── docker └── docker-compose-aspnetcore │ ├── .dockerignore │ ├── docker-compose-aspnetcore.dcproj │ ├── docker-compose.oats.yml │ ├── docker-compose.override.yml │ ├── docker-compose.self-contained.oats.yml │ ├── docker-compose.yml │ ├── launchSettings.json │ ├── oats-template.yml │ └── oats.yaml ├── docs ├── configuration.md ├── installation.md ├── migration.md ├── supported-instrumentations.md └── supported-resource-detectors.md ├── examples └── net8.0 │ └── aspnetcore │ ├── Controllers │ ├── HttpClientController.cs │ ├── MsSqlController.cs │ ├── RedisController.cs │ └── WeatherForecastController.cs │ ├── Dockerfile │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── TodoAppEndpoints.cs │ ├── appsettings.json │ └── aspnetcore.csproj ├── global.json ├── internal ├── RELEASING.md └── img │ └── Grafana_icon.png ├── launchSettings.json ├── package-readme.md ├── scripts ├── run-oats-tests.ps1 └── run-oats-tests.sh ├── src ├── Grafana.OpenTelemetry.Base │ ├── ExporterSettings │ │ ├── AgentOtlpExporter.cs │ │ ├── CloudOtlpExporter.cs │ │ ├── ExporterSettings.cs │ │ ├── GrafanaCloudConfigurationHelper.cs │ │ └── OtlpExporter.cs │ ├── Grafana.OpenTelemetry.Base.csproj │ ├── GrafanaOpenTelemetryEventSource.cs │ ├── GrafanaOpenTelemetryResourceDetector.cs │ ├── GrafanaOpenTelemetrySettings.cs │ ├── Instrumentations │ │ ├── AWSInitializer.cs │ │ ├── AWSLambdaInitializer.cs │ │ ├── AspNetCoreInitializer.cs │ │ ├── AspNetInitializer.cs │ │ ├── CassandraInitializer.cs │ │ ├── ElasticsearchClientInitializer.cs │ │ ├── EntityFrameworkCoreInitializer.cs │ │ ├── GrpcNetClientInitializer.cs │ │ ├── HangfireInitializer.cs │ │ ├── HttpClientInitializer.cs │ │ ├── Instrumentation.cs │ │ ├── InstrumentationInitializer.cs │ │ ├── MySqlDataInitializer.cs │ │ ├── NetRuntimeMetricsInitializer.cs │ │ ├── OwinInitializer.cs │ │ ├── ProcessMetricsInitializer.cs │ │ ├── QuartzInitializer.cs │ │ ├── SqlClientInitializer.cs │ │ ├── StackExchangeRedisInitializer.cs │ │ └── WcfInitializer.cs │ ├── MeterProviderBuilderExtensions.cs │ ├── OpenTelemetryLoggerOptionsExtensions.cs │ ├── ReflectionHelper.cs │ ├── ResourceBuilderExtension.cs │ ├── ResourceDetectors │ │ ├── AWSEBSDetectorInitializer.cs │ │ ├── AWSEC2DetectorInitializer.cs │ │ ├── AWSECSDetectorInitializer.cs │ │ ├── AWSEKSDetectorInitializer.cs │ │ ├── AzureAppServiceDetectorInitializer.cs │ │ ├── AzureContainerAppsDetectorInitializer.cs │ │ ├── AzureVMDetectorInitializer.cs │ │ ├── ContainerResource.cs │ │ ├── HostDetectorInitializer.cs │ │ ├── OperatingSystemResourceInitializer.cs │ │ ├── ProcessResourceInitializer.cs │ │ ├── ProcessRuntimeResource.cs │ │ ├── ResourceDetector.cs │ │ └── ResourceDetectorInitializer.cs │ └── TracerProviderBuilderExtensions.cs └── Grafana.OpenTelemetry │ ├── Grafana.OpenTelemetry.csproj │ ├── OpenTelemetryBuilderExtension.cs │ └── rd.xml └── tests └── Grafana.OpenTelemetry.Tests ├── Grafana.OpenTelemetry.Tests.csproj ├── GrafanaCloudConfigurationHelperTest.cs ├── GrafanaOpenTelemetryResourceDetectorTest.cs ├── GrafanaOpenTelemetrySettingsTest.cs ├── InMemoryResourceExporter.cs ├── InstrumentationTest.cs ├── MeterProviderExtensionsTest.cs ├── OpenTelemetryLoggerOptionsExtensionsTest.cs ├── ReflectionHelperTest.cs ├── ResourceBuilderExtensionTest.cs ├── ResourceDetectorTest.cs └── TracerProviderExtensionsTest.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs 2 | ############################### 3 | # Core EditorConfig Options # 4 | ############################### 5 | # All files 6 | [*] 7 | charset = utf-8 8 | end_of_line = crlf 9 | indent_style = space 10 | indent_size = 2 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.{cs,cshtml,htm,html,md,py,sln,xml}] 15 | indent_size = 4 16 | 17 | ############################### 18 | # .NET Coding Conventions # 19 | ############################### 20 | # Directive order based on dotnet/runtime settings 21 | # https://github.com/dotnet/runtime/blob/main/.editorconfig 22 | [*.cs] 23 | # New line preferences 24 | csharp_new_line_before_open_brace = all 25 | csharp_new_line_before_else = true 26 | csharp_new_line_before_catch = true 27 | csharp_new_line_before_finally = true 28 | csharp_new_line_before_members_in_object_initializers = true 29 | csharp_new_line_before_members_in_anonymous_types = true 30 | csharp_new_line_between_query_expression_clauses = true 31 | 32 | # Indentation preferences 33 | csharp_indent_case_contents = true 34 | csharp_indent_switch_labels = true 35 | csharp_indent_labels = flush_left 36 | 37 | # Modifier preferences 38 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion 39 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 40 | 41 | # this. preferences 42 | dotnet_style_qualification_for_field = true:suggestion 43 | dotnet_style_qualification_for_property = true:suggestion 44 | dotnet_style_qualification_for_method = true:suggestion 45 | dotnet_style_qualification_for_event = true:suggestion 46 | 47 | # var preferences, language keywords vs BCL types preferences 48 | csharp_style_var_for_built_in_types = true:silent 49 | csharp_style_var_when_type_is_apparent = true:silent 50 | csharp_style_var_elsewhere = true:silent 51 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 52 | dotnet_style_predefined_type_for_member_access = true:silent 53 | 54 | # name all constant fields using PascalCase 55 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 56 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 57 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 58 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 59 | dotnet_naming_symbols.constant_fields.required_modifiers = const 60 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = * 61 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 62 | 63 | # Code style defaults 64 | dotnet_sort_system_directives_first = true 65 | csharp_prefer_braces = true:silent 66 | csharp_preserve_single_line_blocks = true 67 | csharp_preserve_single_line_statements = true 68 | csharp_prefer_simple_using_statement = true:suggestion 69 | dotnet_style_readonly_field = true:suggestion 70 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion 71 | dotnet_style_prefer_simplified_interpolation = true:suggestion 72 | dotnet_style_object_initializer = true:suggestion 73 | 74 | # Expression-level preferences 75 | dotnet_style_object_initializer = true:suggestion 76 | dotnet_style_collection_initializer = true:suggestion 77 | dotnet_style_explicit_tuple_names = true:suggestion 78 | dotnet_style_coalesce_expression = true:suggestion 79 | dotnet_style_null_propagation = true:suggestion 80 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent 81 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 82 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 83 | dotnet_style_prefer_auto_properties = true:silent 84 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 85 | dotnet_style_prefer_conditional_expression_over_return = true:silent 86 | csharp_prefer_simple_default_expression = true:suggestion 87 | 88 | # Expression-bodied members 89 | csharp_style_expression_bodied_methods = false:silent 90 | csharp_style_expression_bodied_constructors = false:silent 91 | csharp_style_expression_bodied_operators = false:silent 92 | csharp_style_expression_bodied_properties = true:silent 93 | csharp_style_expression_bodied_indexers = true:silent 94 | csharp_style_expression_bodied_accessors = true:silent 95 | 96 | # Pattern matching 97 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 98 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 99 | csharp_style_inlined_variable_declaration = true:suggestion 100 | 101 | # Null checking preferences 102 | csharp_style_throw_expression = true:suggestion 103 | csharp_style_conditional_delegate_call = true:suggestion 104 | 105 | # Other features 106 | csharp_style_prefer_index_operator = false:none 107 | csharp_style_prefer_range_operator = false:none 108 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 109 | csharp_style_deconstructed_variable_declaration = true:suggestion 110 | csharp_style_namespace_declarations = file_scoped:suggestion 111 | 112 | # Space preferences 113 | csharp_space_after_cast = false 114 | csharp_space_after_colon_in_inheritance_clause = true 115 | csharp_space_after_keywords_in_control_flow_statements = true 116 | csharp_space_around_binary_operators = before_and_after 117 | csharp_space_before_colon_in_inheritance_clause = true 118 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 119 | csharp_space_between_method_call_name_and_opening_parenthesis = false 120 | csharp_space_between_method_call_parameter_list_parentheses = false 121 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 122 | csharp_space_between_method_declaration_parameter_list_parentheses = false 123 | csharp_space_between_parentheses = false 124 | 125 | # Parentheses preferences 126 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 127 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 128 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 129 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 130 | 131 | # Code analyzers 132 | # CA1031: Do not catch general exception types 133 | dotnet_diagnostic.CA1031.severity = none 134 | 135 | # CA1303: Do not pass literals as localized parameters 136 | dotnet_diagnostic.CA1303.severity = none 137 | 138 | # IDE0001: Simplify name 139 | dotnet_diagnostic.IDE0001.severity = warning 140 | 141 | # IDE0002: Simplify member access 142 | dotnet_diagnostic.IDE0002.severity = warning 143 | 144 | # IDE0005: Remove unnecessary import 145 | dotnet_diagnostic.IDE0005.severity = warning 146 | 147 | # RS0041: Public members should not use oblivious types 148 | dotnet_diagnostic.RS0041.severity = suggestion 149 | 150 | [obj/**.cs] 151 | generated_code = true 152 | 153 | [*.csproj] 154 | indent_size = 2 155 | 156 | [*.cshtml.cs] 157 | dotnet_diagnostic.SA1649.severity = none 158 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | # https://git-scm.com/docs/gitignore#_pattern_format 3 | 4 | * @grafana/otel-sdk 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | labels: bug 5 | --- 6 | 7 | # Bug Report 8 | 9 | A list of [Grafana](https://www.nuget.org/profiles/Grafana) and 10 | [OpenTelemetry](https://www.nuget.org/profiles/OpenTelemetry) NuGet packages 11 | used, for example `Grafana.OpenTelemetry 0.6.0-beta.1`): 12 | 13 | * 14 | 15 | The .NET runtime version (e.g. `net462`, `net48`, `netcoreapp3.1`, `net8.0` 16 | etc.). You can find this information in your `*.csproj` file): 17 | 18 | * 19 | 20 | ## Symptom 21 | 22 | A clear and concise description of what the bug is. 23 | 24 | **What is the expected behavior?** 25 | 26 | What did you expect to see? 27 | 28 | **What is the actual behavior?** 29 | 30 | What did you see instead? 31 | 32 | ## Reproduce 33 | 34 | Create a self-contained project using the template of your choice, apply the 35 | minimum required code to result in the issue you're observing. 36 | 37 | ## Additional Context 38 | 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | --- 6 | 7 | # Feature Request 8 | 9 | Before opening a feature request against this project, consider whether the feature 10 | should/could be implemented in the [upstream OpenTelemetry .NET components](https://github.com/open-telemetry/opentelemetry-dotnet) 11 | If so, please [open an issue open-telemetry/opentelemetry-dotnet](https://github.com/open-telemetry/opentelemetry-dotnet/issues/new). 12 | 13 | If the feature is relevant for OpenTelemetry implementations of all languages, 14 | please [open an issue on 15 | opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification/issues/new). 16 | 17 | **Is your feature request related to a problem?** 18 | 19 | If so, provide a concise description of the problem. 20 | 21 | **Describe the solution you'd like:** 22 | 23 | What do you want to happen instead? What is the expected behavior? 24 | 25 | **Describe alternatives you've considered.** 26 | 27 | Which alternative solutions or features have you considered? 28 | 29 | ## Additional Context 30 | 31 | Add any other context about the feature request here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Create a question to help us improve our knowledge base and documentation 4 | labels: question 5 | --- 6 | 7 | # Question 8 | 9 | Use [Github Discussions](https://github.com/grafana/grafana-opentelemetry-dotnet/discussions/new). 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | ## Changes 4 | 5 | Please provide a brief description of the changes here. 6 | 7 | ## Merge requirement checklist 8 | 9 | * [ ] Unit tests added/updated 10 | * [ ] [`CHANGELOG.md`](https://github.com/grafana/grafana-opentelemetry-dotnet) file updated for non-trivial changes 11 | * [ ] Changes in public API reviewed (if applicable) 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | labels: 8 | - "infra" 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - '**.md' 8 | pull_request: 9 | branches: [ main ] 10 | workflow_dispatch: 11 | 12 | env: 13 | DOTNET_CLI_TELEMETRY_OPTOUT: true 14 | DOTNET_NOLOGO: true 15 | DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 16 | NUGET_XMLDOC_MODE: skip 17 | TERM: xterm 18 | 19 | permissions: {} 20 | 21 | jobs: 22 | build-test: 23 | name: ${{ matrix.os-name }} 24 | runs-on: ${{ matrix.runner }} 25 | 26 | outputs: 27 | dotnet-sdk-version: ${{ steps.setup-dotnet.outputs.dotnet-version }} 28 | 29 | permissions: 30 | attestations: write 31 | contents: write 32 | id-token: write 33 | 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | include: 38 | - os-name: macos 39 | runner: macos-latest 40 | - os-name: linux 41 | runner: ubuntu-latest 42 | - os-name: windows 43 | runner: windows-latest 44 | 45 | steps: 46 | 47 | - name: Checkout code 48 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 49 | with: 50 | fetch-depth: 0 51 | filter: 'tree:0' 52 | show-progress: false 53 | 54 | - name: Setup .NET SDK 55 | uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 56 | id: setup-dotnet 57 | 58 | - name: Build 59 | run: dotnet build --configuration Release 60 | 61 | - name: Test 62 | run: dotnet test --configuration Release --logger:"GitHubActions;report-warnings=false" 63 | 64 | - name: Generate SBOM 65 | uses: anchore/sbom-action@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0 66 | with: 67 | artifact-name: build-${{ matrix.os-name }}.spdx.json 68 | output-file: ./artifacts/build.spdx.json 69 | path: ./artifacts/bin 70 | upload-release-assets: ${{ runner.os == 'Windows' }} 71 | 72 | - name: Attest artifacts 73 | uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 74 | if: | 75 | runner.os == 'Windows' && 76 | github.event.repository.fork == false && 77 | (github.ref_name == github.event.repository.default_branch || startsWith(github.ref, 'refs/tags/')) 78 | with: 79 | subject-path: | 80 | ./artifacts/bin/Grafana.OpenTelemetry*/release*/Grafana.OpenTelemetry*.dll 81 | ./artifacts/package/release/* 82 | 83 | - name: Publish NuGet packages 84 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 85 | with: 86 | name: packages-${{ matrix.os-name }} 87 | path: ./artifacts/package/release 88 | if-no-files-found: error 89 | 90 | validate-packages: 91 | needs: build-test 92 | runs-on: ubuntu-latest 93 | steps: 94 | 95 | - name: Download packages 96 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 97 | with: 98 | name: packages-windows 99 | 100 | - name: Setup .NET SDK 101 | uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 102 | with: 103 | dotnet-version: ${{ needs.build-test.outputs.dotnet-sdk-version }} 104 | 105 | - name: Validate NuGet packages 106 | shell: pwsh 107 | run: | 108 | dotnet tool install --global dotnet-validate --version 0.0.1-preview.304 --allow-roll-forward 109 | $packages = Get-ChildItem -Filter "*.nupkg" | ForEach-Object { $_.FullName } 110 | $invalidPackages = 0 111 | foreach ($package in $packages) { 112 | dotnet validate package local $package 113 | if ($LASTEXITCODE -ne 0) { 114 | $invalidPackages++ 115 | } 116 | } 117 | if ($invalidPackages -gt 0) { 118 | Write-Output "::error::$invalidPackages NuGet package(s) failed validation." 119 | exit 1 120 | } 121 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-format.yml: -------------------------------------------------------------------------------- 1 | name: dotnet format 2 | 3 | on: 4 | push: 5 | branches: [ 'main*' ] 6 | paths: 7 | - '**.cs' 8 | - '.editorconfig' 9 | pull_request: 10 | branches: [ 'main*' ] 11 | paths: 12 | - '**.cs' 13 | - '.editorconfig' 14 | 15 | jobs: 16 | check-format: 17 | runs-on: windows-latest 18 | permissions: {} 19 | 20 | steps: 21 | - name: check out code 22 | uses: actions/checkout@v4 23 | with: 24 | persist-credentials: false 25 | 26 | - name: dotnet format 27 | run: dotnet format --verify-no-changes 28 | -------------------------------------------------------------------------------- /.github/workflows/markdown-link-check.yml: -------------------------------------------------------------------------------- 1 | name: markdown-link-check 2 | 3 | on: 4 | push: 5 | branches: [ 'main*' ] 6 | paths: 7 | - '**.md' 8 | pull_request: 9 | branches: [ 'main*' ] 10 | paths: 11 | - '**.md' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | permissions: {} 17 | 18 | steps: 19 | - name: check out code 20 | uses: actions/checkout@v4 21 | with: 22 | persist-credentials: false 23 | 24 | - name: install markdown-link-check 25 | run: sudo npm install -g markdown-link-check 26 | 27 | - name: run markdown-link-check 28 | run: "find . -name '*.md' -print0 | xargs -0 -n1 markdown-link-check --config .markdown_link_check_config.json" 29 | -------------------------------------------------------------------------------- /.github/workflows/markdownlint.yml: -------------------------------------------------------------------------------- 1 | name: markdownlint 2 | 3 | on: 4 | push: 5 | branches: [ 'main*' ] 6 | paths: 7 | - '**.md' 8 | pull_request: 9 | branches: [ 'main*' ] 10 | paths: 11 | - '**.md' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | permissions: {} 17 | 18 | steps: 19 | - name: check out code 20 | uses: actions/checkout@v4 21 | with: 22 | persist-credentials: false 23 | 24 | - name: install markdownlint-cli 25 | run: sudo npm install -g markdownlint-cli 26 | 27 | - name: run markdownlint 28 | run: markdownlint . 29 | -------------------------------------------------------------------------------- /.github/workflows/misspell.yml: -------------------------------------------------------------------------------- 1 | name: sanitycheck 2 | 3 | on: 4 | push: 5 | branches: [ 'main*' ] 6 | pull_request: 7 | branches: [ 'main*' ] 8 | 9 | jobs: 10 | misspell: 11 | runs-on: ubuntu-latest 12 | permissions: {} 13 | 14 | steps: 15 | - name: check out code 16 | uses: actions/checkout@v4 17 | with: 18 | persist-credentials: false 19 | 20 | - name: install misspell 21 | run: | 22 | curl -L -o ./install-misspell.sh https://git.io/misspell 23 | sh ./install-misspell.sh 24 | 25 | - name: run misspell 26 | run: ./bin/misspell -error . 27 | -------------------------------------------------------------------------------- /.github/workflows/oats.yml: -------------------------------------------------------------------------------- 1 | name: OATS 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - '**/*.gitattributes' 8 | - '**/*.gitignore' 9 | - '**/*.md' 10 | pull_request: 11 | branches: [ main ] 12 | workflow_dispatch: 13 | 14 | permissions: {} 15 | 16 | jobs: 17 | acceptance-tests: 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 20 20 | 21 | steps: 22 | 23 | - name: Checkout code 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | with: 26 | filter: 'tree:0' 27 | persist-credentials: false 28 | show-progress: false 29 | 30 | - name: Set up Go 31 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 32 | with: 33 | go-version: '1.24' 34 | 35 | - name: Install OATS 36 | env: 37 | # renovate: datasource=github-releases depName=oats packageName=grafana/oats 38 | OATS_VERSION: v0.3.2 39 | run: go install "github.com/grafana/oats@${OATS_VERSION}" 40 | 41 | - name: Run acceptance tests 42 | run: oats --timeout=5m ./docker/docker-compose-aspnetcore 43 | 44 | - name: Upload log file 45 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 46 | if: failure() 47 | with: 48 | name: docker-compose.log 49 | path: oats/yaml/build/**/output.log 50 | if-no-files-found: ignore 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Aa]rtifacts/ 21 | [Bb]uild/ 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | x64/ 27 | x86/ 28 | [Ww][Ii][Nn]32/ 29 | [Aa][Rr][Mm]/ 30 | [Aa][Rr][Mm]64/ 31 | bld/ 32 | [Bb]in/ 33 | [Oo]bj/ 34 | [Ll]og/ 35 | [Ll]ogs/ 36 | 37 | # Visual Studio 2015/2017 cache/options directory 38 | .vs/ 39 | # Uncomment if you have tasks that create the project's static files in wwwroot 40 | #wwwroot/ 41 | 42 | # Visual Studio 2017 auto generated files 43 | Generated\ Files/ 44 | 45 | # MSTest test Results 46 | [Tt]est[Rr]esult*/ 47 | [Bb]uild[Ll]og.* 48 | 49 | # NUnit 50 | *.VisualState.xml 51 | TestResult.xml 52 | nunit-*.xml 53 | 54 | # Build Results of an ATL Project 55 | [Dd]ebugPS/ 56 | [Rr]eleasePS/ 57 | dlldata.c 58 | 59 | # Benchmark Results 60 | BenchmarkDotNet.Artifacts/ 61 | 62 | # .NET 63 | project.lock.json 64 | project.fragment.lock.json 65 | artifacts/ 66 | 67 | # Tye 68 | .tye/ 69 | 70 | # ASP.NET Scaffolding 71 | ScaffoldingReadMe.txt 72 | 73 | # StyleCop 74 | StyleCopReport.xml 75 | 76 | # Files built by Visual Studio 77 | *_i.c 78 | *_p.c 79 | *_h.h 80 | *.ilk 81 | *.meta 82 | *.obj 83 | *.iobj 84 | *.pch 85 | *.pdb 86 | *.ipdb 87 | *.pgc 88 | *.pgd 89 | *.rsp 90 | *.sbr 91 | *.tlb 92 | *.tli 93 | *.tlh 94 | *.tmp 95 | *.tmp_proj 96 | *_wpftmp.csproj 97 | *.log 98 | *.tlog 99 | *.vspscc 100 | *.vssscc 101 | .builds 102 | *.pidb 103 | *.svclog 104 | *.scc 105 | 106 | # Chutzpah Test files 107 | _Chutzpah* 108 | 109 | # Visual C++ cache files 110 | ipch/ 111 | *.aps 112 | *.ncb 113 | *.opendb 114 | *.opensdf 115 | *.sdf 116 | *.cachefile 117 | *.VC.db 118 | *.VC.VC.opendb 119 | 120 | # Visual Studio profiler 121 | *.psess 122 | *.vsp 123 | *.vspx 124 | *.sap 125 | 126 | # Visual Studio Trace Files 127 | *.e2e 128 | 129 | # TFS 2012 Local Workspace 130 | $tf/ 131 | 132 | # Guidance Automation Toolkit 133 | *.gpState 134 | 135 | # ReSharper is a .NET coding add-in 136 | _ReSharper*/ 137 | *.[Rr]e[Ss]harper 138 | *.DotSettings.user 139 | 140 | # TeamCity is a build add-in 141 | _TeamCity* 142 | 143 | # DotCover is a Code Coverage Tool 144 | *.dotCover 145 | 146 | # AxoCover is a Code Coverage Tool 147 | .axoCover/* 148 | !.axoCover/settings.json 149 | 150 | # Coverlet is a free, cross platform Code Coverage Tool 151 | coverage*.json 152 | coverage*.xml 153 | coverage*.info 154 | 155 | # Visual Studio code coverage results 156 | *.coverage 157 | *.coveragexml 158 | 159 | # NCrunch 160 | _NCrunch_* 161 | .*crunch*.local.xml 162 | nCrunchTemp_* 163 | 164 | # MightyMoose 165 | *.mm.* 166 | AutoTest.Net/ 167 | 168 | # Web workbench (sass) 169 | .sass-cache/ 170 | 171 | # Installshield output folder 172 | [Ee]xpress/ 173 | 174 | # DocProject is a documentation generator add-in 175 | DocProject/buildhelp/ 176 | DocProject/Help/*.HxT 177 | DocProject/Help/*.HxC 178 | DocProject/Help/*.hhc 179 | DocProject/Help/*.hhk 180 | DocProject/Help/*.hhp 181 | DocProject/Help/Html2 182 | DocProject/Help/html 183 | 184 | # Click-Once directory 185 | publish/ 186 | 187 | # Publish Web Output 188 | *.[Pp]ublish.xml 189 | *.azurePubxml 190 | # Note: Comment the next line if you want to checkin your web deploy settings, 191 | # but database connection strings (with potential passwords) will be unencrypted 192 | *.pubxml 193 | *.publishproj 194 | 195 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 196 | # checkin your Azure Web App publish settings, but sensitive information contained 197 | # in these scripts will be unencrypted 198 | PublishScripts/ 199 | 200 | # NuGet Packages 201 | *.nupkg 202 | # NuGet Symbol Packages 203 | *.snupkg 204 | # The packages folder can be ignored because of Package Restore 205 | **/[Pp]ackages/* 206 | # except build/, which is used as an MSBuild target. 207 | !**/[Pp]ackages/build/ 208 | # Uncomment if necessary however generally it will be regenerated when needed 209 | #!**/[Pp]ackages/repositories.config 210 | # NuGet v3's project.json files produces more ignorable files 211 | *.nuget.props 212 | *.nuget.targets 213 | 214 | # Microsoft Azure Build Output 215 | csx/ 216 | *.build.csdef 217 | 218 | # Microsoft Azure Emulator 219 | ecf/ 220 | rcf/ 221 | 222 | # Windows Store app package directories and files 223 | AppPackages/ 224 | BundleArtifacts/ 225 | Package.StoreAssociation.xml 226 | _pkginfo.txt 227 | *.appx 228 | *.appxbundle 229 | *.appxupload 230 | 231 | # Visual Studio cache files 232 | # files ending in .cache can be ignored 233 | *.[Cc]ache 234 | # but keep track of directories ending in .cache 235 | !?*.[Cc]ache/ 236 | 237 | # Others 238 | ClientBin/ 239 | ~$* 240 | *~ 241 | *.dbmdl 242 | *.dbproj.schemaview 243 | *.jfm 244 | *.pfx 245 | *.publishsettings 246 | orleans.codegen.cs 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # SQLLite files 267 | *.db 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | 400 | ## 401 | ## Visual studio for Mac 402 | ## 403 | 404 | 405 | # globs 406 | Makefile.in 407 | *.userprefs 408 | *.usertasks 409 | config.make 410 | config.status 411 | aclocal.m4 412 | install-sh 413 | autom4te.cache/ 414 | *.tar.gz 415 | tarballs/ 416 | test-results/ 417 | 418 | # Mac bundle stuff 419 | *.dmg 420 | *.app 421 | 422 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 423 | # General 424 | .DS_Store 425 | .AppleDouble 426 | .LSOverride 427 | 428 | # Icon must end with two \r 429 | Icon 430 | 431 | 432 | # Thumbnails 433 | ._* 434 | 435 | # Files that might appear in the root of a volume 436 | .DocumentRevisions-V100 437 | .fseventsd 438 | .Spotlight-V100 439 | .TemporaryItems 440 | .Trashes 441 | .VolumeIcon.icns 442 | .com.apple.timemachine.donotpresent 443 | 444 | # Directories potentially created on remote AFP share 445 | .AppleDB 446 | .AppleDesktop 447 | Network Trash Folder 448 | Temporary Items 449 | .apdisk 450 | 451 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 452 | # Windows thumbnail cache files 453 | Thumbs.db 454 | ehthumbs.db 455 | ehthumbs_vista.db 456 | 457 | # Dump file 458 | *.stackdump 459 | 460 | # Folder config file 461 | [Dd]esktop.ini 462 | 463 | # Recycle Bin used on file shares 464 | $RECYCLE.BIN/ 465 | 466 | # Windows Installer files 467 | *.cab 468 | *.msi 469 | *.msix 470 | *.msm 471 | *.msp 472 | 473 | # Windows shortcuts 474 | *.lnk 475 | -------------------------------------------------------------------------------- /.markdown_link_check_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "retryOn429": true, 3 | "timeout": "30s", 4 | "aliveStatusCodes": [200, 403] 5 | } 6 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # Default state for all rules 2 | default: true 3 | 4 | # allow long lines for tables and code blocks 5 | MD013: 6 | code_blocks: false 7 | tables: false 8 | 9 | # Allow same headers for non-siblings, e. g. in the CHANGELOG 10 | MD024: 11 | siblings_only: true 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "EditorConfig.EditorConfig", 4 | "github.vscode-github-actions", 5 | "ms-dotnettools.csharp" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 4 | Grafana Labs 5 | SHA256 6 | Grafana Labs 7 | true 8 | Copyright (c) Grafana Labs 9 | true 10 | true 11 | false 12 | en-US 13 | 14 | $(NoWarn);NU5104 15 | disable 16 | Grafana_icon.png 17 | Apache-2.0 18 | https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/ 19 | package-readme.md 20 | https://github.com/grafana/grafana-opentelemetry-dotnet/blob/main/CHANGELOG.md 21 | false 22 | OpenTelemetry;Grafana;Metrics;Logs;Traces;Observability;Monitoring 23 | true 24 | git 25 | https://github.com/grafana/grafana-opentelemetry-dotnet 26 | snupkg 27 | true 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /GrafanaOpenTelemetry.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F0687CB8-95E1-4372-9444-70676DE3A34A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grafana.OpenTelemetry", "src\Grafana.OpenTelemetry\Grafana.OpenTelemetry.csproj", "{B4761520-2B6F-4605-BC3B-66710F7439EA}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grafana.OpenTelemetry.Base", "src\Grafana.OpenTelemetry.Base\Grafana.OpenTelemetry.Base.csproj", "{8CA452C6-61DA-49A9-8AA7-909D48E7ACF0}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FB0399BE-6925-42B7-8431-C5A6E21DC8EC}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grafana.OpenTelemetry.Tests", "tests\Grafana.OpenTelemetry.Tests\Grafana.OpenTelemetry.Tests.csproj", "{30810D69-3237-4260-93C2-DC601C5AC80F}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{2DC924CF-282A-446D-B94B-D2931E5C6130}" 17 | EndProject 18 | Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose-aspnetcore", "docker\docker-compose-aspnetcore\docker-compose-aspnetcore.dcproj", "{A4C41298-ED4F-4A4C-9B18-014986186C71}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{E63943FA-D9F6-4DC4-91EE-D0BD0BF8E324}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net8.0", "net8.0", "{74BA358B-6EDB-463F-8AB2-313FAA4DE564}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "aspnetcore", "examples\net8.0\aspnetcore\aspnetcore.csproj", "{BD1FB154-C711-4E37-947D-063F5DC4BF9E}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {B4761520-2B6F-4605-BC3B-66710F7439EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {B4761520-2B6F-4605-BC3B-66710F7439EA}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {B4761520-2B6F-4605-BC3B-66710F7439EA}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {B4761520-2B6F-4605-BC3B-66710F7439EA}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {8CA452C6-61DA-49A9-8AA7-909D48E7ACF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {8CA452C6-61DA-49A9-8AA7-909D48E7ACF0}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {8CA452C6-61DA-49A9-8AA7-909D48E7ACF0}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {8CA452C6-61DA-49A9-8AA7-909D48E7ACF0}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {30810D69-3237-4260-93C2-DC601C5AC80F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {30810D69-3237-4260-93C2-DC601C5AC80F}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {30810D69-3237-4260-93C2-DC601C5AC80F}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {30810D69-3237-4260-93C2-DC601C5AC80F}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {A4C41298-ED4F-4A4C-9B18-014986186C71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {A4C41298-ED4F-4A4C-9B18-014986186C71}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {A4C41298-ED4F-4A4C-9B18-014986186C71}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {A4C41298-ED4F-4A4C-9B18-014986186C71}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {BD1FB154-C711-4E37-947D-063F5DC4BF9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {BD1FB154-C711-4E37-947D-063F5DC4BF9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {BD1FB154-C711-4E37-947D-063F5DC4BF9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {BD1FB154-C711-4E37-947D-063F5DC4BF9E}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(NestedProjects) = preSolution 57 | {B4761520-2B6F-4605-BC3B-66710F7439EA} = {F0687CB8-95E1-4372-9444-70676DE3A34A} 58 | {8CA452C6-61DA-49A9-8AA7-909D48E7ACF0} = {F0687CB8-95E1-4372-9444-70676DE3A34A} 59 | {30810D69-3237-4260-93C2-DC601C5AC80F} = {FB0399BE-6925-42B7-8431-C5A6E21DC8EC} 60 | {A4C41298-ED4F-4A4C-9B18-014986186C71} = {E63943FA-D9F6-4DC4-91EE-D0BD0BF8E324} 61 | {74BA358B-6EDB-463F-8AB2-313FAA4DE564} = {2DC924CF-282A-446D-B94B-D2931E5C6130} 62 | {BD1FB154-C711-4E37-947D-063F5DC4BF9E} = {74BA358B-6EDB-463F-8AB2-313FAA4DE564} 63 | EndGlobalSection 64 | EndGlobal 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grafana OpenTelemetry distribution for .NET 2 | 3 | 4 |

5 | Grafana logo 6 | OpenTelemetry logo 7 |

8 | 9 | 10 | 11 | [![Build][ci-badge]][ci-status] 12 | [![OATS][oats-badge]][oats-status] 13 | [![NuGet][package-badge-version]][package-download] 14 | [![SDK][otel-badge]][otel] 15 | [![Slack][slack-badge]][slack-channel] 16 | 17 | 18 | ## About 19 | 20 | This is a pre-configured and pre-packaged bundle of [OpenTelemetry .NET components][otel-contrib], 21 | optimized for [Grafana Cloud Application Observability][app-o11y]. 22 | 23 | It requires only minimal setup and configuration and makes it very easy to emit 24 | OpenTelemetry metrics, logs, and traces from your .NET application. 25 | 26 | ## Getting Started 27 | 28 | ### Step 1: Install package 29 | 30 | For installing the distribution with the full set of dependencies, add a 31 | reference to the [`Grafana.OpenTelemetry`][package] package to your project. 32 | 33 | ```sh 34 | dotnet add package Grafana.OpenTelemetry 35 | ``` 36 | 37 | ### Step 2: Enable the Grafana distribution at application startup 38 | 39 | The `UseGrafana` extension method on the `TracerProviderBuilder` or the 40 | `MetricProviderBuilder` can be used to set up the Grafana distribution. By 41 | default, telemetry data will be sent to Grafana Alloy or an OpenTelemetry collector 42 | that runs locally and listens to default OTLP ports. 43 | 44 | ```csharp 45 | using var tracerProvider = Sdk.CreateTracerProviderBuilder() 46 | .UseGrafana() 47 | .Build(); 48 | ``` 49 | 50 | Alternatively, you can send telemetry data directly to Grafana Cloud without 51 | involving an agent or collector. This can be configured via the environment 52 | variables `OTEL_EXPORTER_OTLP_PROTOCOL`, `OTEL_EXPORTER_OTLP_ENDPOINT`, and 53 | `OTEL_EXPORTER_OTLP_HEADERS`. 54 | 55 | For details on how to obtain those values, refer to 56 | [Send data to the Grafana Cloud OTLP endpoint: Quickstart architecture][push-oltp]. 57 | 58 | ```sh 59 | export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" 60 | export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-gateway-prod-eu-west-0.grafana.net/otlp" 61 | export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic a-secret-token" 62 | ``` 63 | 64 | ## Documentation 65 | 66 | For detailed documentation and setup instructions, refer to the following 67 | documents: 68 | 69 | * [Installation](./docs/installation.md) 70 | * [Configuration](./docs/configuration.md) 71 | * [Supported instrumentations](./docs/supported-instrumentations.md) 72 | * [Supported resource detectors](./docs/supported-resource-detectors.md) 73 | * [Migrating to upstream](./docs/migration.md) 74 | 75 | ## Troubleshooting 76 | 77 | This project utilizes the [self-diagnostics feature of the .NET OpenTelemetry SDK][self-diagnostics]. 78 | 79 | To enable self-diagnostics, go to the [current working directory][working-dir] of 80 | your process and create a configuration file named `OTEL_DIAGNOSTICS.json` with 81 | the following content: 82 | 83 | ```json 84 | { 85 | "LogDirectory": ".", 86 | "FileSize": 32768, 87 | "LogLevel": "Warning" 88 | } 89 | ``` 90 | 91 | To disable self-diagnostics, delete the above file. 92 | 93 | ## Community 94 | 95 | To engage with the Grafana Application Observability community: 96 | 97 | * Chat with us on our community Slack channel. To invite yourself to the 98 | Grafana Slack, visit 99 | and join the [#application-observability][slack-channel] channel. 100 | * Ask questions on the [Discussions page][discussions]. 101 | * [File an issue][issues] for bugs, issues, and feature suggestions. 102 | 103 | [app-o11y]: https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/ 104 | [ci-badge]: https://github.com/grafana/grafana-opentelemetry-dotnet/actions/workflows/ci.yml/badge.svg?branch=main 105 | [ci-status]: https://github.com/grafana/grafana-opentelemetry-dotnet/actions/workflows/ci.yml 106 | [discussions]: https://github.com/grafana/grafana-opentelemetry-dotnet/discussions 107 | [issues]: https://github.com/grafana/grafana-opentelemetry-dotnet/issues/new 108 | [oats-badge]: https://github.com/grafana/grafana-opentelemetry-dotnet/actions/workflows/oats.yml/badge.svg?branch=main 109 | [oats-status]: https://github.com/grafana/grafana-opentelemetry-dotnet/actions/workflows/oats.yml 110 | [otel]: https://github.com/open-telemetry/opentelemetry-dotnet 111 | [otel-badge]: https://img.shields.io/badge/OTel--SDK-1.9.0-blue?style=flat&logo=opentelemetry 112 | [otel-contrib]: http://github.com/open-telemetry/opentelemetry-dotnet-contrib 113 | [package]: https://www.nuget.org/packages/Grafana.OpenTelemetry 114 | [package-badge-version]: https://img.shields.io/nuget/v/Grafana.OpenTelemetry?logo=nuget&label=NuGet&color=blue 115 | [package-download]: https://www.nuget.org/profiles/Grafana 116 | [push-oltp]: https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/#quickstart-architecture 117 | [self-diagnostics]: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry/README.md#self-diagnostics 118 | [slack-badge]: https://img.shields.io/badge/%20Slack-%23app--o11y-brightgreen.svg?logo=slack 119 | [slack-channel]: https://grafana.slack.com/archives/C05E87XRK3J 120 | [working-dir]: https://en.wikipedia.org/wiki/Working_directory 121 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/docker-compose-aspnetcore.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1 5 | Linux 6 | a4c41298-ed4f-4a4c-9b18-014986186c71 7 | LaunchBrowser 8 | {Scheme}://localhost:{ServicePort}/swagger 9 | aspnetcore 10 | 11 | 12 | 13 | 14 | docker-compose.yml 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/docker-compose.oats.yml: -------------------------------------------------------------------------------- 1 | services: 2 | aspnetcore: 3 | image: ${DOCKER_REGISTRY-}aspnetcore 4 | build: 5 | context: ../.. 6 | dockerfile: examples/net8.0/aspnetcore/Dockerfile 7 | environment: 8 | - OTEL_EXPORTER_OTLP_ENDPOINT=http://lgtm:4317 9 | ports: 10 | - "5000:8080" 11 | - "8080:8080" # for OATs 12 | depends_on: 13 | - redis 14 | - mssql 15 | redis: 16 | image: redis:7.2 17 | ports: 18 | - "6379:6379" 19 | healthcheck: 20 | test: ["CMD-SHELL", "redis-cli ping | grep PONG"] 21 | interval: 1s 22 | timeout: 3s 23 | retries: 5 24 | mssql: 25 | image: mcr.microsoft.com/mssql/server:2022-latest 26 | ports: 27 | - "1433:1433" 28 | environment: 29 | - ACCEPT_EULA=Y 30 | - MSSQL_SA_PASSWORD=Password12345%% 31 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | services: 2 | aspnetcore: 3 | environment: 4 | - ASPNETCORE_ENVIRONMENT=Development 5 | ports: 6 | - "8080" 7 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/docker-compose.self-contained.oats.yml: -------------------------------------------------------------------------------- 1 | services: 2 | aspnetcore: 3 | image: ${DOCKER_REGISTRY-}aspnetcore 4 | build: 5 | context: ../.. 6 | dockerfile: examples/net8.0/aspnetcore/Dockerfile 7 | args: 8 | DOTNET_PUBLISH_ARGS: "--self-contained true /p:PublishSingleFile=true" 9 | entrypoint: ./aspnetcore 10 | environment: 11 | - OTEL_EXPORTER_OTLP_ENDPOINT=http://lgtm:4317 12 | ports: 13 | - "5000:8080" 14 | - "8080:8080" # for OATs 15 | depends_on: 16 | - redis 17 | - mssql 18 | redis: 19 | image: redis:7.2 20 | ports: 21 | - "6379:6379" 22 | healthcheck: 23 | test: ["CMD-SHELL", "redis-cli ping | grep PONG"] 24 | interval: 1s 25 | timeout: 3s 26 | retries: 5 27 | mssql: 28 | image: mcr.microsoft.com/mssql/server:2022-latest 29 | ports: 30 | - "1433:1433" 31 | environment: 32 | - ACCEPT_EULA=Y 33 | - MSSQL_SA_PASSWORD=Password12345%% 34 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | aspnetcore: 3 | image: ${DOCKER_REGISTRY-}aspnetcore 4 | build: 5 | context: ../.. 6 | dockerfile: examples/net8.0/aspnetcore/Dockerfile 7 | depends_on: 8 | - redis 9 | - mssql 10 | ports: 11 | - "8080:8080" # for OATs 12 | redis: 13 | image: redis:7.2 14 | ports: 15 | - "6379:6379" 16 | mssql: 17 | image: mcr.microsoft.com/mssql/server:2022-latest 18 | ports: 19 | - "1433:1433" 20 | environment: 21 | - ACCEPT_EULA=Y 22 | - MSSQL_SA_PASSWORD=Password12345%% 23 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Docker Compose": { 4 | "commandName": "DockerCompose", 5 | "commandVersion": "1.0", 6 | "serviceActions": { 7 | "aspnet": "StartDebugging", 8 | "aspnetcore": "StartDebugging" 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/oats-template.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | - name: default 3 | docker-compose: 4 | generator: docker-lgtm 5 | files: 6 | - ./docker-compose.oats.yml 7 | - name: self-contained 8 | docker-compose: 9 | generator: docker-lgtm 10 | files: 11 | - ./docker-compose.self-contained.oats.yml 12 | interval: 500ms 13 | -------------------------------------------------------------------------------- /docker/docker-compose-aspnetcore/oats.yaml: -------------------------------------------------------------------------------- 1 | include: 2 | - ./oats-template.yml 3 | input: 4 | - path: /api/HttpClient/Get 5 | - path: /api/HttpClient/GetError 6 | - path: /api/MsSql/ServerInfo 7 | - path: /api/MsSql/Tables 8 | - path: /api/Redis/LeftPush 9 | - path: /api/todo/items 10 | expected: 11 | logs: 12 | - logql: '{ service_name="aspnetcore" } |~ `Application started.`' 13 | contains: 14 | - 'Application started' 15 | metrics: 16 | - promql: http_client_request_duration_seconds_count{} 17 | value: '>= 0' 18 | - promql: http_client_request_duration_seconds_count{http_request_method="GET", http_response_status_code="200"} 19 | value: '>= 0' 20 | traces: 21 | - traceql: '{ resource.process.pid > 0 }' 22 | spans: 23 | - name: 'GET' 24 | - traceql: '{ span.http.route =~ "api/HttpClient/Get" }' 25 | spans: 26 | - name: 'GET api/HttpClient/Get' 27 | attributes: 28 | http.request.method: GET 29 | - name: 'GET api/HttpClient/Get' 30 | attributes: 31 | service.name: aspnetcore 32 | service.version: 1.0.0.0 33 | telemetry.distro.name: grafana-opentelemetry-dotnet 34 | telemetry.distro.version: regex:.+ 35 | deployment.environment: production 36 | process.runtime.description: regex:.NET.+ 37 | process.runtime.name: .NET 38 | process.runtime.version: regex:.+ 39 | host.name: regex:.+ 40 | telemetry.sdk.name: opentelemetry 41 | telemetry.sdk.language: dotnet 42 | telemetry.sdk.version: regex:.+ 43 | - traceql: '{ span.http.route =~ "api/HttpClient/GetError" }' 44 | spans: 45 | - name: 'GET api/HttpClient/GetError' 46 | attributes: 47 | http.request.method: GET 48 | - name: 'GET' 49 | attributes: 50 | error.type: '500' 51 | http.request.method: GET 52 | http.response.status_code: '500' 53 | - traceql: '{ span.http.route =~ "api/MsSql/ServerInfo" }' 54 | spans: 55 | - name: 'master' 56 | attributes: 57 | db.system: mssql 58 | db.name: master 59 | db.statement: sp_server_info 60 | otel.library.name: OpenTelemetry.Instrumentation.SqlClient 61 | - traceql: '{ span.http.route =~ "api/MsSql/Tables" }' 62 | spans: 63 | - name: 'master' 64 | attributes: 65 | db.system: mssql 66 | db.name: master 67 | otel.library.name: OpenTelemetry.Instrumentation.SqlClient 68 | - traceql: '{ span.http.route =~ "api/Redis/LeftPush" }' 69 | spans: 70 | - name: 'LPUSH' 71 | attributes: 72 | db.statement: LPUSH 73 | db.system: redis 74 | net.peer.name: redis 75 | - traceql: '{ span.http.route =~ "/api/todo/items/" }' 76 | spans: 77 | - name: 'main' 78 | attributes: 79 | db.system: sqlite 80 | db.name: main 81 | peer.service: /app/App_Data/TodoApp.db 82 | otel.library.name: OpenTelemetry.Instrumentation.EntityFrameworkCore 83 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installing the Grafana OpenTelemetry distribution for .NET 2 | 3 | * [Supported .NET Versions](#supported-net-versions) 4 | * [Install the full package with all available instrumentations](#install-the-full-package-with-all-available-instrumentations) 5 | * [Install the base package](#install-the-base-package) 6 | * [Minimizing unneeded dependencies](#minimizing-unneeded-dependencies) 7 | 8 | ## Supported .NET Versions 9 | 10 | The packages shipped from this repository generally support all the officially 11 | supported versions of [.NET](https://dotnet.microsoft.com/download/dotnet) and 12 | [.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework) (an 13 | older Windows-based .NET implementation), except `.NET Framework 3.5`. 14 | 15 | Some instrumentations and instrumentation libraries referenced by the 16 | distribution don't support both .NET and .NET Framework, but only one of them. 17 | For details, refer to the list of [supported instrumentations](./supported-instrumentations.md). 18 | 19 | ## Install the full package with all available instrumentations 20 | 21 | For installing the distribution with the full set of dependencies, add a 22 | reference to the [`Grafana.OpenTelemetry`](https://www.nuget.org/packages/Grafana.OpenTelemetry) 23 | package to your project. 24 | 25 | ```sh 26 | dotnet add package --prerelease Grafana.OpenTelemetry 27 | ``` 28 | 29 | The list of [supported instrumentations](./supported-instrumentations.md) 30 | specifies what instrumentations are included in the full package. 31 | 32 | ## Install the base package 33 | 34 | For installing the distribution with a minimal set of dependencies, add a 35 | reference to the [`Grafana.OpenTelemetry.Base`](https://www.nuget.org/packages/Grafana.OpenTelemetry.Base) 36 | package to your project. 37 | 38 | ```sh 39 | dotnet add package --prerelease Grafana.OpenTelemetry.Base 40 | ``` 41 | 42 | The list of [supported instrumentations](./supported-instrumentations.md) and 43 | [supported resource detectors](./supported-resource-detectors.md) 44 | specify which are included in the base package and enabled by default. 45 | 46 | ## Minimizing unneeded dependencies 47 | 48 | Users might utilize some instrumentation libraries the [full package](#install-the-full-package-with-all-available-instrumentations) 49 | contains, while other contained libraries will not be needed. However, the 50 | [full package](#install-the-full-package-with-all-available-instrumentations) 51 | still pulls in those unneeded instrumentations libraries with their 52 | dependencies. 53 | 54 | To mitigate this situation, [base package](#install-the-base-package) 55 | with a built-in lazy-loading mechanism can be used. This mechanism will 56 | initialize known available instrumentation library or resource detectors 57 | assembly, regardless of whether it's added as dependency of the [full package](#install-the-full-package-with-all-available-instrumentations) 58 | or as part of the instrumented project. 59 | 60 | For example, if it is desired to use the `AspNetCore` instrumentation without 61 | pulling in any other dependencies from the [full package](#install-the-full-package-with-all-available-instrumentations), 62 | it suffices to install the `AspNetCore` instrumentation library along with the 63 | base package. 64 | 65 | ```sh 66 | dotnet add package --prerelease Grafana.OpenTelemetry.Base 67 | dotnet add package --prerelease OpenTelemetry.Instrumentation.AspNetCore 68 | ``` 69 | 70 | Then, the `AspNetCore` instrumentation will be lazy-loaded during the 71 | invocation of the `UseGrafana` extension method, no further code changes are 72 | necessary. 73 | 74 | ```csharp 75 | using var tracerProvider = Sdk.CreateTracerProviderBuilder() 76 | .UseGrafana() 77 | .Build(); 78 | ``` 79 | 80 | This way, any other [instrumentation library](./supported-instrumentations.md) 81 | or [resource detector](./supported-resource-detectors.md) supported by the 82 | distribution can be added via lazy loading. 83 | -------------------------------------------------------------------------------- /docs/migration.md: -------------------------------------------------------------------------------- 1 | # Migrating to OpenTelemetry .NET 2 | 3 | All functionality provided by the distribution can also be utilized by manually 4 | installing and configuring upstream OpenTelemetry .NET packages. Follow these 5 | steps if you want to migrate from this distribution to the upstream 6 | OpenTelemetry .NET project. 7 | 8 | - Set appropriate environment variables or `web.config`/`app.config` values as 9 | explained here: [Send data to the Grafana Cloud OTLP endpoint][publish-otlp] 10 | - Install and activate the OpenTelemetry SDK, 11 | as covered in the upstream [Getting Started][getting-started] guide 12 | - Setup the OTLP exporter for metrics, logs, and traces - see the 13 | [exporter README][exporter-readme] for details. The exporter will respect the 14 | previously set environment variables: 15 | - `OTEL_EXPORTER_OTLP_PROTOCOL` 16 | - `OTEL_EXPORTER_OTLP_ENDPOINT` 17 | - `OTEL_EXPORTER_OTLP_HEADERS` 18 | - Install and configure any desired instrumentation packages 19 | [listed here](./supported-instrumentations.md) 20 | - [Add the OpenTelemetry resource attributes][resource-attributes] 21 | via the `OTEL_RESOURCE_ATTRIBUTES` environment variable 22 | - `service.name` 23 | - `service.namespace` 24 | - `service.instance.id` 25 | - `service.version` 26 | - `deployment.environment` 27 | 28 | ```shell 29 | export OTEL_RESOURCE_ATTRIBUTES=service.instance.id=,deployment.environment=... 30 | ``` 31 | 32 | [exporter-readme]: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md 33 | [getting-started]: https://github.com/open-telemetry/opentelemetry-dotnet#getting-started 34 | [publish-otlp]: https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/#quickstart-architecture 35 | [resource-attributes]: https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/instrument/resource-attributes/ 36 | -------------------------------------------------------------------------------- /docs/supported-instrumentations.md: -------------------------------------------------------------------------------- 1 | # Supported instrumentations 2 | 3 | At the moment, the following instrumentations are included in the distribution 4 | packages with [full](./installation.md#install-the-full-package-with-all-available-instrumentations) 5 | and [minimal](./installation.md#install-the-base-package) dependencies: 6 | 7 | | Identifier | Full | Base | Library name | 8 | | --------------------- | ------------------ | ------------------ | ------------ | 9 | | `AspNet` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.AspNet](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet) | 10 | | `AspNetCore` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.AspNetCore](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) | 11 | | `AWS` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.AWS](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AWS) | 12 | | `AWSLambda` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.AWSLambda](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AWSLambda) | 13 | | `AWSResource` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.AWS](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.AWS) | 14 | | `AzureResource` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Azure](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.Azure) | 15 | | `Cassandra` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.Cassandra](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Cassandra) | 16 | | `ContainerResource` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Container](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.Container) | 17 | | `ElasticsearchClient` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.ElasticsearchClient](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.ElasticsearchClient) | 18 | | `EntityFrameworkCore` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.EntityFrameworkCore](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.EntityFrameworkCore) | 19 | | `GrpcNetClient` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.GrpcNetClient](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.GrpcNetClient) | 20 | | `Hangfire` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.Hangfire](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Hangfire) | 21 | | `HttpClient` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.Http](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Http) | 22 | | `HostResource` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Host](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.Host) | 23 | | `MySqlData` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.MySqlData](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.MySqlData) | 24 | | | | | [MySql.Data.OpenTelemetry](https://www.nuget.org/packages/MySql.Data.OpenTelemetry) | 25 | | `NetRuntime` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.Runtime](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Runtime) | 26 | | `Owin` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.Owin](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Owin) | 27 | | `Process` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.Process](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Process) | 28 | | `ProcessResource` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Process](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.Process) | 29 | | `ProcessRuntimeResource`| :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.ProcessRuntime](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Resources.ProcessRuntime) | 30 | | `Quartz` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.Quartz](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Quartz) | 31 | | `SqlClient` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Instrumentation.SqlClient](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.SqlClient) | 32 | | `StackExchangeRedis` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.StackExchangeRedis](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.StackExchangeRedis) | 33 | | `Wcf` | :heavy_check_mark: | | [OpenTelemetry.Instrumentation.Wcf](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Wcf) | 34 | 35 | * The `AWSLambda` instrumentation is included but needs to be explicitly 36 | activated, as activating it in non-AWS scenarios causes errors. 37 | * The `ContainerResource` instrumentation is included but needs to be explicitly 38 | activated, as it currently adds container resource attributes for processes 39 | running not in containers. 40 | * The `MySqlData` instrumentation does not include the MySql.Data.OpenTelemetry 41 | package. Install separately if needed. 42 | -------------------------------------------------------------------------------- /docs/supported-resource-detectors.md: -------------------------------------------------------------------------------- 1 | # Supported resource detectors 2 | 3 | The following resource detectors are recognized: 4 | 5 | | Identifier | Enabled by default | Pre-installed | Library name | 6 | | --------------------- | ------------------ | ------------------ | ------------ | 7 | | `AWSEBS` | | | [OpenTelemetry.Resources.AWS](https://www.nuget.org/packages/OpenTelemetry.Resources.AWS) | 8 | | `AWSEC2` | | | [OpenTelemetry.Resources.AWS](https://www.nuget.org/packages/OpenTelemetry.Resources.AWS) | 9 | | `AWSECS` | | | [OpenTelemetry.Resources.AWS](https://www.nuget.org/packages/OpenTelemetry.Resources.AWS) | 10 | | `AWSEKS` | | | [OpenTelemetry.Resources.AWS](https://www.nuget.org/packages/OpenTelemetry.Resources.AWS) | 11 | | `AzureAppService` | | | [OpenTelemetry.Resources.Azure](https://www.nuget.org/packages/OpenTelemetry.Resources.Azure) | 12 | | `AzureVM` | | | [OpenTelemetry.Resources.Azure](https://www.nuget.org/packages/OpenTelemetry.Resources.Azure) | 13 | | `AzureContainerApps` | | | [OpenTelemetry.Resources.Azure](https://www.nuget.org/packages/OpenTelemetry.Resources.Azure) | 14 | | `Container` | | :heavy_check_mark: | [OpenTelemetry.Resources.Container](https://www.nuget.org/packages/OpenTelemetry.Resources.Container) | 15 | | `Host` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Host](https://www.nuget.org/packages/OpenTelemetry.Resources.Host) | 16 | | `OperatingSystem` | | | [OpenTelemetry.Resources.OperatingSystem](https://www.nuget.org/packages/OpenTelemetry.Resources.OperatingSystem) | 17 | | `Process` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.Process](https://www.nuget.org/packages/OpenTelemetry.Resources.Process) | 18 | | `ProcessRuntime` | :heavy_check_mark: | :heavy_check_mark: | [OpenTelemetry.Resources.ProcessRuntime](https://www.nuget.org/packages/OpenTelemetry.Resources.ProcessRuntime) | 19 | 20 | * The `Container` resource detector is included but needs to be explicitly 21 | activated, as activating it in non-container environments can causes erroneous 22 | attributes. A future release may activate it by default. 23 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Controllers/HttpClientController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace aspnetcore.Controllers; 9 | 10 | [Route("api/[controller]/[action]")] 11 | [ApiController] 12 | public class HttpClientController(HttpClient client) : ControllerBase 13 | { 14 | [HttpGet] 15 | public async Task>> Get() 16 | { 17 | var content = await client.GetStringAsync("https://postman-echo.com/get?hello=world"); 18 | return Ok(content); 19 | } 20 | 21 | [HttpGet] 22 | public async Task>> GetError() 23 | { 24 | var response = await client.GetAsync("http://postman-echo.com/status/500"); 25 | var content = await response.Content.ReadAsStringAsync(); 26 | return Ok(content); 27 | } 28 | 29 | [HttpPost] 30 | public async Task> Post() 31 | { 32 | using var body = new StringContent("Hello World"); 33 | using var response = await client.PostAsync("https://postman-echo.com/post", body); 34 | 35 | var content = await response.Content.ReadAsStringAsync(); 36 | 37 | return Ok(content); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Controllers/MsSqlController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System.Data; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Data.SqlClient; 9 | 10 | namespace aspnetcore.Controllers; 11 | 12 | [Route("api/[controller]/[action]")] 13 | [ApiController] 14 | public class MsSqlController(SqlConnection db) : ControllerBase 15 | { 16 | [HttpGet] 17 | public async Task>> Tables() 18 | { 19 | await db.OpenAsync(); 20 | 21 | await using var command = db.CreateCommand(); 22 | 23 | command.CommandText = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"; 24 | 25 | var tables = new List(); 26 | 27 | await using (var reader = await command.ExecuteReaderAsync()) 28 | { 29 | while (await reader.ReadAsync()) 30 | { 31 | tables.Add(reader.GetString(0)); 32 | } 33 | } 34 | 35 | return Ok(tables); 36 | } 37 | 38 | [HttpGet] 39 | public async Task>> ServerInfo() 40 | { 41 | await db.OpenAsync(); 42 | 43 | await using var command = db.CreateCommand(); 44 | 45 | command.CommandText = "sp_server_info"; 46 | command.CommandType = CommandType.StoredProcedure; 47 | 48 | var serverInfo = new List(); 49 | 50 | await using (var reader = await command.ExecuteReaderAsync()) 51 | { 52 | while (await reader.ReadAsync()) 53 | { 54 | serverInfo.Add($"ID={reader.GetInt32(0)} , NAME={reader.GetString(1)} , VALUE={reader.GetString(2)}"); 55 | } 56 | } 57 | 58 | return Ok(serverInfo); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Controllers/RedisController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System.ComponentModel.DataAnnotations; 7 | using Microsoft.AspNetCore.Mvc; 8 | using StackExchange.Redis; 9 | 10 | namespace aspnetcore.Controllers; 11 | 12 | [Route("api/[controller]/[action]")] 13 | [ApiController] 14 | public class RedisController(IDatabase database, ILogger logger) : ControllerBase 15 | { 16 | const string LIST_KEY = "list"; 17 | 18 | [HttpGet] 19 | public async Task> LeftPush() 20 | { 21 | if (!ModelState.IsValid) 22 | { 23 | return StatusCode(500); 24 | } 25 | 26 | var data = new LeftPushBody { Name = "test" }; 27 | var length = await database.ListLeftPushAsync(LIST_KEY, data.Name); 28 | 29 | logger.LogInformation("LeftPush: {Name} - {Length}", data.Name, length); 30 | 31 | return Ok(); 32 | } 33 | 34 | [HttpGet] 35 | public async Task> LeftPop() 36 | { 37 | var value = await database.ListLeftPopAsync(LIST_KEY); 38 | 39 | logger.LogInformation("LeftPop: {Value}", value); 40 | 41 | return Ok(value); 42 | } 43 | 44 | public class LeftPushBody 45 | { 46 | [Required] 47 | public string? Name { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace aspnetcore.Controllers; 9 | 10 | [ApiController] 11 | [Route("[controller]")] 12 | public class WeatherForecastController : ControllerBase 13 | { 14 | private static readonly string[] Summaries = 15 | [ 16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 17 | ]; 18 | 19 | [HttpGet(Name = "GetWeatherForecast")] 20 | public IEnumerable Get() 21 | { 22 | return [.. Enumerable.Range(1, 5).Select(index => new WeatherForecast 23 | { 24 | Date = DateTime.Now.AddDays(index), 25 | TemperatureC = Random.Shared.Next(-20, 55), 26 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 27 | })]; 28 | } 29 | 30 | public class WeatherForecast 31 | { 32 | public DateTime Date { get; set; } 33 | public int TemperatureC { get; set; } 34 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 35 | public string? Summary { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build 2 | ARG TARGETARCH 3 | ARG CONFIGURATION="Release" 4 | ARG DOTNET_PUBLISH_ARGS="" 5 | 6 | COPY . /source 7 | WORKDIR /source 8 | 9 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 10 | 11 | RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ 12 | dotnet publish "examples/net8.0/aspnetcore/aspnetcore.csproj" --arch "${TARGETARCH}" --configuration "${CONFIGURATION}" --output /app ${DOTNET_PUBLISH_ARGS} 13 | 14 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS final 15 | WORKDIR /app 16 | EXPOSE 8080 17 | 18 | COPY --from=build /app . 19 | ENTRYPOINT ["dotnet", "aspnetcore.dll"] 20 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using aspnetcore; 7 | using Grafana.OpenTelemetry; 8 | using Microsoft.Data.SqlClient; 9 | using OpenTelemetry.Logs; 10 | using OpenTelemetry.Trace; 11 | using StackExchange.Redis; 12 | 13 | var builder = WebApplication.CreateBuilder(args); 14 | 15 | builder.Logging.AddOpenTelemetry(builder => builder.UseGrafana()); 16 | 17 | builder.Services.AddOpenTelemetry() 18 | .WithMetrics(builder => builder.UseGrafana()) 19 | .WithTracing(builder => builder.UseGrafana().AddConsoleExporter()); 20 | 21 | // Redis 22 | builder.Services.AddSingleton(sp => ConnectionMultiplexer.Connect("redis:6379")); 23 | builder.Services.AddScoped(sp => sp.GetRequiredService().GetDatabase()); 24 | 25 | // Microsoft SQL Server 26 | builder.Services.AddTransient(sp => 27 | { 28 | var connectionString = "Server=mssql,1433;Database=master;User=sa;Password=Password12345%%;Encrypt=False;TrustServerCertificate=True"; 29 | return new SqlConnection(connectionString); 30 | }); 31 | 32 | builder.Services.AddHttpClient(); 33 | 34 | builder.Services.AddControllers(); 35 | builder.Services.AddTodoApp(); 36 | 37 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 38 | builder.Services.AddEndpointsApiExplorer(); 39 | builder.Services.AddSwaggerGen(); 40 | 41 | var app = builder.Build(); 42 | 43 | app.UseSwagger(); 44 | app.UseSwaggerUI(); 45 | 46 | app.UseAuthorization(); 47 | app.MapControllers(); 48 | app.MapTodoApp(); 49 | 50 | app.MapGet("/", () => Results.Redirect("/swagger")); 51 | 52 | app.Run(); 53 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "aspnetcore": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "swagger", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "dotnetRunMessages": true, 11 | "applicationUrl": "http://localhost:5125" 12 | }, 13 | "IIS Express": { 14 | "commandName": "IISExpress", 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "Docker": { 22 | "commandName": "Docker", 23 | "launchBrowser": true, 24 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 25 | "environmentVariables": { 26 | "ASPNETCORE_URLS": "http://+:80" 27 | }, 28 | "publishAllPorts": true 29 | } 30 | }, 31 | "$schema": "https://json.schemastore.org/launchsettings.json", 32 | "iisSettings": { 33 | "windowsAuthentication": false, 34 | "anonymousAuthentication": true, 35 | "iisExpress": { 36 | "applicationUrl": "http://localhost:11318", 37 | "sslPort": 0 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/TodoAppEndpoints.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace aspnetcore; 9 | 10 | public static class TodoAppEndpoints 11 | { 12 | public static IServiceCollection AddTodoApp(this IServiceCollection services) 13 | { 14 | services.AddSingleton(TimeProvider.System); 15 | services.AddScoped(); 16 | 17 | services.AddDbContext((serviceProvider, options) => 18 | { 19 | var configuration = serviceProvider.GetRequiredService(); 20 | var dataDirectory = configuration["DataDirectory"]; 21 | 22 | if (string.IsNullOrEmpty(dataDirectory) || !Path.IsPathRooted(dataDirectory)) 23 | { 24 | var environment = serviceProvider.GetRequiredService(); 25 | dataDirectory = Path.Combine(environment.ContentRootPath, "App_Data"); 26 | } 27 | 28 | if (!Directory.Exists(dataDirectory)) 29 | { 30 | Directory.CreateDirectory(dataDirectory); 31 | } 32 | 33 | var databaseFile = Path.Combine(dataDirectory, "TodoApp.db"); 34 | 35 | options.UseSqlite("Data Source=" + databaseFile); 36 | }); 37 | 38 | return services; 39 | } 40 | 41 | public static IEndpointRouteBuilder MapTodoApp(this IEndpointRouteBuilder builder) 42 | { 43 | var todos = builder.MapGroup("/api/todo/items").WithTags("Todo"); 44 | { 45 | todos.MapPost("/", async (CreateTodoItemModel model, TodoRepository repository) => 46 | { 47 | var id = await repository.AddItemAsync(model.Text); 48 | return Results.Created($"/api/items/{id}", new { Id = id }); 49 | }); 50 | 51 | todos.MapGet("/", async (TodoRepository repository) => await repository.GetItemsAsync()); 52 | 53 | todos.MapGet("/{id}", async (Guid id, TodoRepository repository) => await repository.GetItemAsync(id)); 54 | 55 | todos.MapPost("/{id}/complete", async (Guid id, TodoRepository repository) => 56 | { 57 | return await repository.CompleteItemAsync(id) switch 58 | { 59 | true => Results.NoContent(), 60 | false => Results.Problem(statusCode: StatusCodes.Status400BadRequest), 61 | _ => Results.Problem(statusCode: StatusCodes.Status404NotFound), 62 | }; 63 | }); 64 | 65 | todos.MapDelete("/{id}", async (Guid id, TodoRepository repository) => 66 | { 67 | var deleted = await repository.DeleteItemAsync(id); 68 | return deleted switch 69 | { 70 | true => Results.NoContent(), 71 | false => Results.Problem(statusCode: StatusCodes.Status404NotFound), 72 | }; 73 | }); 74 | } 75 | 76 | return builder; 77 | } 78 | 79 | public class TodoContext(DbContextOptions options) : DbContext(options) 80 | { 81 | public DbSet Items { get; set; } = default!; 82 | } 83 | 84 | public class TodoRepository(TimeProvider timeProvider, TodoContext context) 85 | { 86 | public async Task AddItemAsync(string text) 87 | { 88 | await this.EnsureDatabaseAsync(); 89 | 90 | var item = new TodoItem 91 | { 92 | CreatedAt = this.UtcNow(), 93 | Text = text 94 | }; 95 | 96 | context.Add(item); 97 | 98 | await context.SaveChangesAsync(); 99 | 100 | return item; 101 | } 102 | 103 | public async Task CompleteItemAsync(Guid itemId) 104 | { 105 | var item = await this.GetItemAsync(itemId); 106 | 107 | if (item is null) 108 | { 109 | return null; 110 | } 111 | 112 | if (item.CompletedAt.HasValue) 113 | { 114 | return false; 115 | } 116 | 117 | item.CompletedAt = this.UtcNow(); 118 | 119 | context.Items.Update(item); 120 | 121 | await context.SaveChangesAsync(); 122 | 123 | return true; 124 | } 125 | 126 | public async Task DeleteItemAsync(Guid itemId) 127 | { 128 | var item = await this.GetItemAsync(itemId); 129 | 130 | if (item is null) 131 | { 132 | return false; 133 | } 134 | 135 | context.Items.Remove(item); 136 | 137 | await context.SaveChangesAsync(); 138 | 139 | return true; 140 | } 141 | 142 | public async Task GetItemAsync(Guid itemId) 143 | { 144 | await this.EnsureDatabaseAsync(); 145 | 146 | return await context.Items.FindAsync([itemId]); 147 | } 148 | 149 | public async Task> GetItemsAsync() 150 | { 151 | await this.EnsureDatabaseAsync(); 152 | 153 | return await context.Items 154 | .OrderBy(x => x.CompletedAt.HasValue) 155 | .ThenBy(x => x.CreatedAt) 156 | .ToListAsync(); 157 | } 158 | 159 | private async Task EnsureDatabaseAsync() => await context.Database.EnsureCreatedAsync(); 160 | 161 | private DateTime UtcNow() => timeProvider.GetUtcNow().UtcDateTime; 162 | } 163 | 164 | public class CreateTodoItemModel 165 | { 166 | public string Text { get; set; } = string.Empty; 167 | } 168 | 169 | public class TodoItem 170 | { 171 | public Guid Id { get; set; } 172 | 173 | public string Text { get; set; } = default!; 174 | 175 | public DateTime CreatedAt { get; set; } 176 | 177 | public DateTime? CompletedAt { get; set; } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "System": "Warning" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /examples/net8.0/aspnetcore/aspnetcore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Linux 8 | ..\..\.. 9 | ..\..\..\docker-compose-aspnetcore.dcproj 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "rollForward": "latestFeature", 4 | "version": "8.0.410", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /internal/RELEASING.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | This project relies on manual steps to publish the libraries in this repo as 4 | NuGet packages. Package versioning is determined by git tags, and implemented 5 | using [the MinVer package](https://github.com/adamralph/minver). This package 6 | is also used by the OpenTelemetry .NET SDK. 7 | 8 | MinVer finds the latest tag in the repo commit history and uses the tag as 9 | the version when building the libaries and packages. 10 | 11 | NuGet treats any package version containing `-alpha`, `-beta`, or `-rc` as a 12 | *pre-release* package. 13 | 14 | To publish to the Grafana Labs NuGet organization, you must be added as a 15 | member by an existing organization administrator. 16 | 17 | ## Publication Steps 18 | 19 | 1. Determine the version to be released, set a tag on `main` via git: 20 | * `git checkout main` 21 | * `git tag VERSION` where `VERSION` is an appropriate SemVer version 22 | 2. Build both packages: 23 | * `dotnet build ./src/Grafana.OpenTelemetry.Base --configuration Release 24 | -p:Deterministic=true` 25 | * `dotnet build ./src/Grafana.OpenTelemetry --configuration Release 26 | -p:Deterministic=true` 27 | 3. [Open the NuGet package upload page]( 28 | https://www.nuget.org/packages/manage/upload) 29 | 4. Upload the two packages, checking that fields are populated as expected. 30 | The packages will be located at: 31 | * `./src/Grafana.OpenTelemetry.Base/bin/Release/ 32 | Grafana.OpenTelemetry.Base.VERSION.nupkg` 33 | * `./src/Grafana.OpenTelemetry/bin/Release/ 34 | Grafana.OpenTelemetry.VERSION.nupkg` 35 | -------------------------------------------------------------------------------- /internal/img/Grafana_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/grafana-opentelemetry-dotnet/37b41e726f28fdddd8fb4feb403964cc609dc291/internal/img/Grafana_icon.png -------------------------------------------------------------------------------- /launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Docker Compose": { 4 | "commandName": "DockerCompose", 5 | "commandVersion": "1.0", 6 | "serviceActions": { 7 | "aspnetcore": "StartDebugging", 8 | "aspnet": "StartDebugging" 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /package-readme.md: -------------------------------------------------------------------------------- 1 | # Grafana OpenTelemetry distribution for .NET 2 | 3 | 4 | [![NuGet][package-badge-version]][package-download] 5 | [![SDK][otel-badge]][otel] 6 | [![Slack][slack-badge]][slack-channel] 7 | 8 | 9 | ## About 10 | 11 | This is a pre-configured and pre-packaged bundle of [OpenTelemetry .NET components][otel-contrib], 12 | optimized for [Grafana Cloud Application Observability][app-o11y]. 13 | 14 | It requires only minimal setup and configuration and makes it very easy to emit 15 | OpenTelemetry metrics, logs, and traces from your .NET application. 16 | 17 | ## Getting Started 18 | 19 | ### Step 1: Install package 20 | 21 | For installing the distribution with the full set of dependencies, add a 22 | reference to the [`Grafana.OpenTelemetry`][package] package to your project. 23 | 24 | ```sh 25 | dotnet add package Grafana.OpenTelemetry 26 | ``` 27 | 28 | ### Step 2: Enable the Grafana distribution at application startup 29 | 30 | The `UseGrafana` extension method on the `TracerProviderBuilder` or the 31 | `MetricProviderBuilder` can be used to set up the Grafana distribution. By 32 | default, telemetry data will be sent to Grafana Alloy or an OpenTelemetry collector 33 | that runs locally and listens to default OTLP ports. 34 | 35 | ```csharp 36 | using var tracerProvider = Sdk.CreateTracerProviderBuilder() 37 | .UseGrafana() 38 | .Build(); 39 | ``` 40 | 41 | Alternatively, you can send telemetry data directly to Grafana Cloud without 42 | involving an agent or collector. This can be configured via the environment 43 | variables `OTEL_EXPORTER_OTLP_PROTOCOL`, `OTEL_EXPORTER_OTLP_ENDPOINT`, and 44 | `OTEL_EXPORTER_OTLP_HEADERS`. 45 | 46 | For details on how to obtain those values, refer to 47 | [Send data to the Grafana Cloud OTLP endpoint: Quickstart architecture][push-oltp]. 48 | 49 | ## Documentation 50 | 51 | For detailed documentation and setup instructions, refer to [our documentation][docs]. 52 | 53 | [app-o11y]: https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/ 54 | [docs]: https://github.com/grafana/grafana-opentelemetry-dotnet/tree/main/docs 55 | [otel]: https://github.com/open-telemetry/opentelemetry-dotnet 56 | [otel-badge]: https://img.shields.io/badge/OTel--SDK-1.9.0-blue?style=flat&logo=opentelemetry 57 | [otel-contrib]: http://github.com/open-telemetry/opentelemetry-dotnet-contrib 58 | [package]: https://www.nuget.org/packages/Grafana.OpenTelemetry 59 | [package-badge-version]: https://img.shields.io/nuget/v/Grafana.OpenTelemetry?logo=nuget&label=NuGet&color=blue 60 | [package-download]: https://www.nuget.org/profiles/Grafana 61 | [push-oltp]: https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/#quickstart-architecture 62 | [slack-badge]: https://img.shields.io/badge/%20Slack-%23app--o11y-brightgreen.svg?logo=slack 63 | [slack-channel]: https://grafana.slack.com/archives/C05E87XRK3J 64 | -------------------------------------------------------------------------------- /scripts/run-oats-tests.ps1: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env pwsh 2 | 3 | $ErrorActionPreference = "Stop" 4 | $InformationPreference = "Continue" 5 | $ProgressPreference = "SilentlyContinue" 6 | 7 | # renovate: datasource=github-releases depName=oats packageName=grafana/oats 8 | ${env:OATS_VERSION}="v0.3.2" 9 | 10 | go install "github.com/grafana/oats@${env:OATS_VERSION}" 11 | & "${env:GOPATH}/bin/oats" --timeout=5m ./docker/docker-compose-aspnetcore 12 | -------------------------------------------------------------------------------- /scripts/run-oats-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # renovate: datasource=github-releases depName=oats packageName=grafana/oats 6 | export OATS_VERSION=v0.3.2 7 | 8 | go install "github.com/grafana/oats@${OATS_VERSION}" 9 | ${GOPATH}/bin/oats --timeout=5m ./docker/docker-compose-aspnetcore 10 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ExporterSettings/AgentOtlpExporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using OpenTelemetry.Exporter; 8 | using OpenTelemetry.Logs; 9 | using OpenTelemetry.Metrics; 10 | using OpenTelemetry.Trace; 11 | 12 | namespace Grafana.OpenTelemetry 13 | { 14 | /// 15 | /// Settings for exporting telemetry to a Grafana Alloy or collector. 16 | /// 17 | public class AgentOtlpExporter : ExporterSettings 18 | { 19 | /// 20 | /// Gets or sets the address of the Grafana Alloy or collector. If not set, the OpenTelemetry 21 | /// default is used (`http://localhost:4817` for http/protobuf, and `http://localhost:4818` 22 | /// for grpc). 23 | /// 24 | public Uri Endpoint { get; set; } 25 | 26 | /// 27 | /// The OTLP protocol to be used for exporting telemetry data. 28 | /// 29 | public OtlpExportProtocol Protocol { get; set; } 30 | 31 | /// 32 | override internal void Apply(TracerProviderBuilder builder) 33 | { 34 | if (EnableTraces == false) 35 | { 36 | return; 37 | } 38 | 39 | builder.AddOtlpExporter(config => ApplyToConfig(config)); 40 | } 41 | 42 | /// 43 | override internal void Apply(MeterProviderBuilder builder) 44 | { 45 | if (EnableMetrics == false) 46 | { 47 | return; 48 | } 49 | 50 | builder.AddOtlpExporter(config => ApplyToConfig(config)); 51 | } 52 | 53 | /// 54 | override internal void Apply(OpenTelemetryLoggerOptions options) 55 | { 56 | if (EnableLogs == false) 57 | { 58 | return; 59 | } 60 | 61 | options.AddOtlpExporter(config => ApplyToConfig(config)); 62 | } 63 | 64 | private void ApplyToConfig(OtlpExporterOptions options) 65 | { 66 | if (Endpoint != null) 67 | { 68 | options.Endpoint = Endpoint; 69 | } 70 | 71 | if (Protocol != default) 72 | { 73 | options.Protocol = (OtlpExportProtocol)Protocol; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ExporterSettings/CloudOtlpExporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using Microsoft.Extensions.Configuration; 8 | using OpenTelemetry.Exporter; 9 | using OpenTelemetry.Logs; 10 | using OpenTelemetry.Metrics; 11 | using OpenTelemetry.Trace; 12 | 13 | namespace Grafana.OpenTelemetry 14 | { 15 | /// 16 | /// Settings for exporting telemetry directly to Grafana Alloy via OTLP. 17 | /// 18 | [Obsolete("This class is obsolete. Use OtlpExporter instead.")] 19 | public class CloudOtlpExporter : ExporterSettings 20 | { 21 | internal const string ZoneEnvVarName = "GRAFANA_CLOUD_ZONE"; 22 | internal const string InstanceIdEnvVarName = "GRAFANA_CLOUD_INSTANCE_ID"; 23 | internal const string ApiKeyEnvVarName = "GRAFANA_CLOUD_API_KEY"; 24 | 25 | /// 26 | /// Gets or sets the zone of the Grafana Cloud stack. 27 | /// 28 | public string Zone { get; } 29 | 30 | /// 31 | /// Gets or sets the instance id of the Grafana Cloud stack. 32 | /// 33 | public string InstanceId { get; } 34 | 35 | /// 36 | /// Gets or sets the API key for sending data to the Grafana Cloud stack. 37 | /// 38 | public string ApiKey { get; } 39 | 40 | /// 41 | /// Initializes a new instance of . 42 | /// 43 | /// Parameters are set from environment variables. 44 | /// 45 | public CloudOtlpExporter() 46 | : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) 47 | { } 48 | 49 | /// 50 | /// Initializes a new instance of with given 51 | /// parameters. 52 | /// 53 | /// The zone of the Grafana cloud stack 54 | /// The instance id of the Grafana cloud stack 55 | /// The API token for sending data to the Grafana cloud stack 56 | /// 57 | public CloudOtlpExporter(string zone, string instanceId, string apiKey) 58 | { 59 | this.Zone = zone ?? throw new ArgumentNullException(nameof(zone)); 60 | this.InstanceId = instanceId ?? throw new ArgumentNullException(nameof(instanceId)); 61 | this.ApiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey)); 62 | } 63 | 64 | internal CloudOtlpExporter(IConfiguration configuration) 65 | : this(configuration[ZoneEnvVarName], configuration[InstanceIdEnvVarName], configuration[ApiKeyEnvVarName]) 66 | { } 67 | 68 | /// 69 | override internal void Apply(TracerProviderBuilder builder) 70 | { 71 | if (EnableTraces == false) 72 | { 73 | return; 74 | } 75 | 76 | builder.AddOtlpExporter(config => 77 | { 78 | var configurationHelper = new GrafanaCloudConfigurationHelper(Zone, InstanceId, ApiKey); 79 | 80 | config.Endpoint = configurationHelper.OtlpEndpointTraces; 81 | config.Headers = configurationHelper.OtlpAuthorizationHeader; 82 | config.Protocol = OtlpExportProtocol.HttpProtobuf; 83 | }); 84 | } 85 | 86 | /// 87 | override internal void Apply(MeterProviderBuilder builder) 88 | { 89 | if (EnableMetrics == false) 90 | { 91 | return; 92 | } 93 | 94 | builder.AddOtlpExporter(config => 95 | { 96 | var configurationHelper = new GrafanaCloudConfigurationHelper(Zone, InstanceId, ApiKey); 97 | 98 | config.Endpoint = configurationHelper.OtlpEndpointMetrics; 99 | config.Headers = configurationHelper.OtlpAuthorizationHeader; 100 | config.Protocol = OtlpExportProtocol.HttpProtobuf; 101 | }); 102 | } 103 | 104 | /// 105 | override internal void Apply(OpenTelemetryLoggerOptions options) 106 | { 107 | if (EnableLogs == false) 108 | { 109 | return; 110 | } 111 | 112 | options.AddOtlpExporter(config => 113 | { 114 | var configurationHelper = new GrafanaCloudConfigurationHelper(Zone, InstanceId, ApiKey); 115 | 116 | config.Endpoint = configurationHelper.OtlpEndpointLogs; 117 | config.Headers = configurationHelper.OtlpAuthorizationHeader; 118 | config.Protocol = OtlpExportProtocol.HttpProtobuf; 119 | }); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ExporterSettings/ExporterSettings.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Logs; 7 | using OpenTelemetry.Metrics; 8 | using OpenTelemetry.Trace; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | /// 13 | /// All OpenTelemetry exporter settings supported by Grafana need to derive from this class. 14 | /// 15 | public abstract class ExporterSettings 16 | { 17 | /// 18 | /// Gets or sets whether traces should be sent to Grafana. 19 | /// 20 | public bool EnableTraces { get; set; } = true; 21 | 22 | /// 23 | /// Gets or sets whether metrics should be sent to Grafana. 24 | /// 25 | public bool EnableMetrics { get; set; } = true; 26 | 27 | /// 28 | /// Gets or sets whether logs should be sent to Grafana. 29 | /// 30 | public bool EnableLogs { get; set; } = true; 31 | 32 | /// 33 | /// Applies the exporter settings by initializing an exporter on the 34 | /// given . 35 | /// 36 | /// A 37 | internal abstract void Apply(TracerProviderBuilder builder); 38 | 39 | /// 40 | /// Applies the exporter settings by initializing an exporter on the 41 | /// given . 42 | /// 43 | /// A 44 | internal abstract void Apply(MeterProviderBuilder builder); 45 | 46 | /// 47 | /// Applies the exporter settings by initializing an exporter on the 48 | /// given . 49 | /// 50 | /// A instance. 51 | internal abstract void Apply(OpenTelemetryLoggerOptions options); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ExporterSettings/GrafanaCloudConfigurationHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | /// 11 | /// Helper class for Grafana Cloud configuration. 12 | /// 13 | internal class GrafanaCloudConfigurationHelper 14 | { 15 | private const string PathExtensionTraces = "/v1/traces"; 16 | private const string PathExtensionMetrics = "/v1/metrics"; 17 | private const string PathExtensionLogs = "/v1/logs"; 18 | 19 | private string _zone; 20 | private string _instanceId; 21 | private string _apiKey; 22 | 23 | public GrafanaCloudConfigurationHelper(string zone, string instanceId, string apiKey) 24 | { 25 | _zone = zone; 26 | _instanceId = instanceId; 27 | _apiKey = apiKey; 28 | } 29 | 30 | public Uri OtlpEndpointTraces 31 | { 32 | get => new Uri($"{GetOtlpEndpointBase()}{PathExtensionTraces}"); 33 | } 34 | 35 | public Uri OtlpEndpointMetrics 36 | { 37 | get => new Uri($"{GetOtlpEndpointBase()}{PathExtensionMetrics}"); 38 | } 39 | 40 | public Uri OtlpEndpointLogs 41 | { 42 | get => new Uri($"{GetOtlpEndpointBase()}{PathExtensionLogs}"); 43 | } 44 | 45 | public string OtlpAuthorizationHeader 46 | { 47 | get 48 | { 49 | var authorizationValue = Convert.ToBase64String( 50 | System.Text.Encoding.UTF8.GetBytes($"{_instanceId}:{_apiKey}") 51 | ); 52 | 53 | return $"Authorization=Basic {authorizationValue}"; 54 | } 55 | } 56 | 57 | private string GetOtlpEndpointBase() => $"https://otlp-gateway-{_zone}.grafana.net/otlp"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ExporterSettings/OtlpExporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using OpenTelemetry.Exporter; 8 | using OpenTelemetry.Logs; 9 | using OpenTelemetry.Metrics; 10 | using OpenTelemetry.Trace; 11 | 12 | namespace Grafana.OpenTelemetry 13 | { 14 | /// 15 | /// Settings for exporting telemetry via plain OTLP exporter settings. 16 | /// 17 | public class OtlpExporter : ExporterSettings 18 | { 19 | /// 20 | /// Gets or sets the target to which the exporter is going to send telemetry. 21 | /// Must be a valid Uri with scheme (http or https) and host, and 22 | /// may contain a port and path. 23 | /// 24 | public Uri Endpoint { get; set; } 25 | 26 | /// 27 | /// Gets or sets optional headers for the connection. Refer to the specification for information on the expected format for Headers. 28 | /// 29 | public string Headers { get; set; } 30 | 31 | /// 32 | /// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf 33 | /// 34 | public OtlpExportProtocol Protocol { get; set; } 35 | 36 | /// 37 | override internal void Apply(TracerProviderBuilder builder) 38 | { 39 | if (EnableTraces == false) 40 | { 41 | return; 42 | } 43 | 44 | builder.AddOtlpExporter(config => 45 | { 46 | config.Endpoint = new Uri($"{Endpoint}/v1/traces"); 47 | config.Headers = Headers; 48 | config.Protocol = Protocol; 49 | }); 50 | } 51 | 52 | /// 53 | override internal void Apply(MeterProviderBuilder builder) 54 | { 55 | if (EnableMetrics == false) 56 | { 57 | return; 58 | } 59 | 60 | builder.AddOtlpExporter(config => 61 | { 62 | config.Endpoint = new Uri($"{Endpoint}/v1/metrics"); 63 | config.Headers = Headers; 64 | config.Protocol = Protocol; 65 | }); 66 | } 67 | 68 | /// 69 | override internal void Apply(OpenTelemetryLoggerOptions options) 70 | { 71 | if (EnableLogs == false) 72 | { 73 | return; 74 | } 75 | 76 | options.AddOtlpExporter(config => 77 | { 78 | config.Endpoint = new Uri($"{Endpoint}/v1/logs"); 79 | config.Headers = Headers; 80 | config.Protocol = Protocol; 81 | }); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Grafana.OpenTelemetry.Base.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Minimal Grafana distribution of OpenTelemetry .NET 5 | true 6 | net8.0;netstandard2.0;net462 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | <_Parameter1>Grafana.OpenTelemetry.Tests 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/GrafanaOpenTelemetryEventSource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Diagnostics.Tracing; 8 | using System.Text.Json; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | [EventSource(Name = "OpenTelemetry-Grafana-Distribution")] 13 | internal sealed class GrafanaOpenTelemetryEventSource : EventSource 14 | { 15 | public static GrafanaOpenTelemetryEventSource Log = new GrafanaOpenTelemetryEventSource(); 16 | 17 | [NonEvent] 18 | public void EnabledMetricsInstrumentation(string instrumentationLibrary) 19 | { 20 | if (Log.IsEnabled(EventLevel.Informational, EventKeywords.All)) 21 | { 22 | this.EnabledInstrumentation("metrics", instrumentationLibrary); 23 | } 24 | } 25 | 26 | [NonEvent] 27 | public void EnabledTracingInstrumentation(string instrumentationLibrary) 28 | { 29 | if (Log.IsEnabled(EventLevel.Informational, EventKeywords.All)) 30 | { 31 | this.EnabledInstrumentation("tracing", instrumentationLibrary); 32 | } 33 | } 34 | 35 | [NonEvent] 36 | public void FailureEnablingMetricsInstrumentation(string instrumentationLibrary, Exception ex) 37 | { 38 | if (Log.IsEnabled(EventLevel.Warning, EventKeywords.All)) 39 | { 40 | this.FailureEnablingInstrumentation("metrics", instrumentationLibrary, ex.ToString()); 41 | } 42 | } 43 | 44 | [NonEvent] 45 | public void FailureEnablingTracingInstrumentation(string instrumentationLibrary, Exception ex) 46 | { 47 | if (Log.IsEnabled(EventLevel.Warning, EventKeywords.All)) 48 | { 49 | this.FailureEnablingInstrumentation("tracing", instrumentationLibrary, ex.ToString()); 50 | } 51 | } 52 | 53 | [NonEvent] 54 | public void InitializeDistribution(GrafanaOpenTelemetrySettings settings) 55 | { 56 | var settingsJson = JsonSerializer.Serialize(settings); 57 | 58 | InitializeDistribution(settingsJson); 59 | } 60 | 61 | [Event(1, Message = "Grafana distribution initializing with settings: {0}'", Level = EventLevel.Informational)] 62 | public void InitializeDistribution(string settings) 63 | { 64 | this.WriteEvent(1, settings); 65 | } 66 | 67 | [Event(2, Message = "Grafana distribution enabling {0} instrumentation '{1}'", Level = EventLevel.Informational)] 68 | public void EnabledInstrumentation(string signal, string instrumentationLibrary) 69 | { 70 | this.WriteEvent(2, signal, instrumentationLibrary); 71 | } 72 | 73 | [Event(3, Message = "Grafana distribution cannot enable {0} instrumentation '{1}'. Exception: {2}", Level = EventLevel.Warning)] 74 | public void FailureEnablingInstrumentation(string signal, string instrumentationLibrary, string ex) 75 | { 76 | this.WriteEvent(3, signal, instrumentationLibrary, ex); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/GrafanaOpenTelemetryResourceDetector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class GrafanaOpenTelemetryResourceDetector : IResourceDetector 13 | { 14 | internal const string ResourceKey_DistroName = "telemetry.distro.name"; 15 | internal const string ResourceKey_DistroVersion = "telemetry.distro.version"; 16 | internal const string ResourceKey_DeploymentEnvironment = "deployment.environment"; 17 | internal const string ResourceValue_DistroName = "grafana-opentelemetry-dotnet"; 18 | 19 | private GrafanaOpenTelemetrySettings _settings; 20 | 21 | public GrafanaOpenTelemetryResourceDetector(GrafanaOpenTelemetrySettings settings) 22 | { 23 | _settings = settings; 24 | } 25 | 26 | public Resource Detect() 27 | { 28 | var attributes = new List>(new KeyValuePair[] 29 | { 30 | new KeyValuePair(ResourceKey_DistroName, ResourceValue_DistroName), 31 | new KeyValuePair(ResourceKey_DistroVersion, GetDistroVersion()), 32 | new KeyValuePair(ResourceKey_DeploymentEnvironment, _settings.DeploymentEnvironment) 33 | }); 34 | 35 | attributes.AddRange(_settings.ResourceAttributes); 36 | 37 | return new Resource(attributes); 38 | } 39 | 40 | static internal string GetDistroVersion() 41 | { 42 | var informationalVersion = typeof(GrafanaOpenTelemetryResourceDetector) 43 | .Assembly 44 | .GetCustomAttribute()? 45 | .InformationalVersion; 46 | 47 | if (string.IsNullOrWhiteSpace(informationalVersion)) 48 | { 49 | informationalVersion = "0.0.0"; 50 | } 51 | 52 | // A Git hash is appended to the informational version after a "+" character. That's of limited use and 53 | // therefore removed here. 54 | return informationalVersion.Split('+')[0]; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/GrafanaOpenTelemetrySettings.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Reflection; 9 | using Microsoft.Extensions.Configuration; 10 | 11 | namespace Grafana.OpenTelemetry 12 | { 13 | /// 14 | /// Settings for configuring the OpenTelemetry .NET distribution for Grafana. 15 | /// 16 | public class GrafanaOpenTelemetrySettings 17 | { 18 | internal const string DisableInstrumentationsEnvVarName = "GRAFANA_DOTNET_DISABLE_INSTRUMENTATIONS"; 19 | internal const string DisableResourceDetectorsEnvVarName = "GRAFANA_DOTNET_DISABLE_RESOURCE_DETECTORS"; 20 | internal const string ResourceDetectorsEnvVarName = "GRAFANA_DOTNET_RESOURCE_DETECTORS"; 21 | internal const string ServiceNameEnvVarName = "OTEL_SERVICE_NAME"; 22 | 23 | /// 24 | /// Gets or sets the exporter settings for sending telemetry data to Grafana. 25 | /// 26 | /// By default, this is set to: 27 | /// 1. j initialized by environment variables, or if 28 | /// the environment variables aren't present, it is set to 29 | /// 2. a with the default OTLP endpoint. 30 | /// 31 | /// If set to `null`, no exporter will be initialized by the OpenTelemetry .NET 32 | /// distribution for Grafana. 33 | /// 34 | public ExporterSettings ExporterSettings { get; set; } 35 | 36 | /// 37 | /// Gets the list of instrumentations to be activated. 38 | /// 39 | /// By default, all available instrumentations are activated. 40 | /// 41 | public HashSet Instrumentations { get; } = new HashSet((Instrumentation[])Enum.GetValues(typeof(Instrumentation))); 42 | 43 | /// 44 | /// Gets the list of resource detectors to be activated. 45 | /// 46 | /// By default, all only resource detectors that do not impact application startup are activated. 47 | /// 48 | public HashSet ResourceDetectors { get; } = new HashSet(new ResourceDetector[] 49 | { 50 | // Activating the container resource detector by default always populates a `container.id` attribute, 51 | // even when running in a non-container Linux setting. 52 | // ResourceDetector.Container, 53 | ResourceDetector.Host, 54 | ResourceDetector.Process, 55 | ResourceDetector.ProcessRuntime, 56 | }); 57 | 58 | /// 59 | /// Gets or sets the logical name of the service to be instrumented. 60 | /// 61 | /// This corresponds to the `service.name` resource attribute. 62 | /// 63 | public string ServiceName { get; set; } = Assembly.GetEntryAssembly()?.GetName().Name ?? System.Diagnostics.Process.GetCurrentProcess().ProcessName; 64 | 65 | /// 66 | /// Gets or sets the version of the service to be instrumented. 67 | /// 68 | /// This corresponds to the `service.version` resource attribute. 69 | /// 70 | public string ServiceVersion { get; set; } = Assembly.GetEntryAssembly()?.GetName().Version.ToString() ?? "unknown"; 71 | 72 | /// 73 | /// Gets or sets the string id of the service instance. 74 | /// 75 | /// This corresponds to the `service.instance.id` resource attribute. 76 | /// 77 | public string ServiceInstanceId { get; set; } 78 | 79 | /// 80 | /// Gets or sets the name of the deployment environment ("staging" or "production"). 81 | /// 82 | public string DeploymentEnvironment { get; set; } = "production"; 83 | 84 | /// 85 | /// Gets a dictionary of custom resource attributes. 86 | /// 87 | public IDictionary ResourceAttributes { get; } = new Dictionary(); 88 | 89 | /// 90 | /// Initializes an instance of . 91 | /// 92 | public GrafanaOpenTelemetrySettings() 93 | : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) 94 | { } 95 | 96 | internal GrafanaOpenTelemetrySettings(IConfiguration configuration) 97 | { 98 | try 99 | { 100 | #pragma warning disable CS0618 101 | ExporterSettings = new CloudOtlpExporter(); 102 | #pragma warning restore CS0618 103 | } 104 | catch (Exception) 105 | { 106 | ExporterSettings = new AgentOtlpExporter(); 107 | } 108 | 109 | 110 | // Activating AWSLambda instrumentation by default causes the provider builder to bail in case certain 111 | // Lambda-specific environment variables are missing. 112 | // 113 | // De-activate it until the related issue is resolved: https://github.com/grafana/app-o11y/issues/378 114 | Instrumentations.Remove(Instrumentation.AWSLambda); 115 | 116 | var disableInstrumentations = configuration[DisableInstrumentationsEnvVarName]; 117 | 118 | if (!string.IsNullOrEmpty(disableInstrumentations)) 119 | { 120 | foreach (var instrumentationStr in disableInstrumentations.Split(new char[] { ',', ':' })) 121 | { 122 | if (Enum.TryParse(instrumentationStr, out var instrumentation)) 123 | { 124 | Instrumentations.Remove(instrumentation); 125 | } 126 | } 127 | } 128 | 129 | var resourceDetectors = configuration[ResourceDetectorsEnvVarName]; 130 | 131 | if (!string.IsNullOrEmpty(resourceDetectors)) 132 | { 133 | ResourceDetectors.Clear(); 134 | 135 | foreach (var resourceDetectorStr in resourceDetectors.Split(new char[] { ',', ':' })) 136 | { 137 | if (Enum.TryParse(resourceDetectorStr, out var resourceDetector)) 138 | { 139 | ResourceDetectors.Add(resourceDetector); 140 | } 141 | } 142 | } 143 | 144 | var disableResourceDetectors = configuration[DisableResourceDetectorsEnvVarName]; 145 | 146 | if (!string.IsNullOrEmpty(disableResourceDetectors)) 147 | { 148 | foreach (var resourceDetectorStr in disableResourceDetectors.Split(new char[] { ',', ':' })) 149 | { 150 | if (Enum.TryParse(resourceDetectorStr, out var resourceDetector)) 151 | { 152 | ResourceDetectors.Remove(resourceDetector); 153 | } 154 | } 155 | } 156 | 157 | var serviceName = configuration[ServiceNameEnvVarName]; 158 | 159 | if (!string.IsNullOrEmpty(serviceName)) 160 | { 161 | ServiceName = serviceName; 162 | } 163 | 164 | // Set deployment environment from known environment variables. 165 | foreach (var envVarName in new string[] { "DOTNET_ENVIRONMENT", "ASPNETCORE_ENVIRONMENT" }) 166 | { 167 | var deploymentEnvironment = Environment.GetEnvironmentVariable(envVarName); 168 | 169 | if (deploymentEnvironment != null) 170 | { 171 | DeploymentEnvironment = deploymentEnvironment.ToLower(); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/AWSInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class AWSInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.AWS; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.AWS", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddAWSInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/AWSLambdaInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class AWSLambdaInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.AWSLambda; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.AWSLambda", 18 | "OpenTelemetry.Instrumentation.AWSLambda.TracerProviderBuilderExtensions", 19 | "AddAWSLambdaConfigurations", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/AspNetCoreInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | using OpenTelemetry.Trace; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AspNetCoreInitializer : InstrumentationInitializer 12 | { 13 | public override Instrumentation Id { get; } = Instrumentation.AspNetCore; 14 | 15 | protected override void InitializeTracing(TracerProviderBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Instrumentation.AspNetCore", 19 | "OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions", 20 | "AddAspNetCoreInstrumentation", 21 | new object[] { builder }); 22 | } 23 | 24 | protected override void InitializeMetrics(MeterProviderBuilder builder) 25 | { 26 | ReflectionHelper.CallStaticMethod( 27 | "OpenTelemetry.Instrumentation.AspNetCore", 28 | "OpenTelemetry.Metrics.AspNetCoreInstrumentationMeterProviderBuilderExtensions", 29 | "AddAspNetCoreInstrumentation", 30 | new object[] { builder }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/AspNetInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | using OpenTelemetry.Trace; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AspNetInitializer : InstrumentationInitializer 12 | { 13 | public override Instrumentation Id { get; } = Instrumentation.AspNet; 14 | 15 | protected override void InitializeTracing(TracerProviderBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Instrumentation.AspNet", 19 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 20 | "AddAspNetInstrumentation", 21 | new object[] { builder }); 22 | } 23 | 24 | protected override void InitializeMetrics(MeterProviderBuilder builder) 25 | { 26 | ReflectionHelper.CallStaticMethod( 27 | "OpenTelemetry.Instrumentation.AspNet", 28 | "OpenTelemetry.Metrics.MeterProviderBuilderExtensions", 29 | "AddAspNetInstrumentation", 30 | new object[] { builder }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/CassandraInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class CassandraInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Cassandra; 13 | 14 | protected override void InitializeMetrics(MeterProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.Cassandra", 18 | "OpenTelemetry.Metrics.MeterProviderBuilderExtensions", 19 | "AddCassandraInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/ElasticsearchClientInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class ElasticsearchClientInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.ElasticsearchClient; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.ElasticsearchClient", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddElasticsearchClientInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/EntityFrameworkCoreInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class EntityFrameworkCoreInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.EntityFrameworkCore; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.EntityFrameworkCore", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddEntityFrameworkCoreInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/GrpcNetClientInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class GrpcNetClientInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.GrpcNetClient; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | builder.AddGrpcClientInstrumentation(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/HangfireInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class HangfireInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Hangfire; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.Hangfire", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddHangfireInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/HttpClientInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | using OpenTelemetry.Trace; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class HttpClientInitializer : InstrumentationInitializer 12 | { 13 | public override Instrumentation Id { get; } = Instrumentation.HttpClient; 14 | 15 | protected override void InitializeTracing(TracerProviderBuilder builder) 16 | { 17 | builder.AddHttpClientInstrumentation(); 18 | } 19 | 20 | protected override void InitializeMetrics(MeterProviderBuilder builder) 21 | { 22 | builder.AddHttpClientInstrumentation(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/Instrumentation.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | namespace Grafana.OpenTelemetry 7 | { 8 | /// 9 | /// An enum representing different instrumentation libraries. 10 | /// 11 | public enum Instrumentation 12 | { 13 | /// 14 | /// .NET runtime metrics (OpenTelemetry.Instrumentation.Runtime) 15 | /// 16 | NetRuntime, 17 | 18 | /// 19 | /// .NET process metrics (OpenTelemetry.Instrumentation.Process) 20 | /// 21 | Process, 22 | 23 | /// 24 | /// HttpClient metrics and traces (OpenTelemetry.Instrumentation.Http) 25 | /// 26 | HttpClient, 27 | 28 | /// 29 | /// ASP.NET Core metrics and traces (OpenTelemetry.Instrumentation.AspNetCore) 30 | /// 31 | AspNetCore, 32 | 33 | /// 34 | /// gRPC client traces (OpenTelemetry.Instrumentation.GrpcNetClient) 35 | /// 36 | GrpcNetClient, 37 | 38 | /// 39 | /// SQL client traces (OpenTelemetry.Instrumentation.SqlClient) 40 | /// 41 | SqlClient, 42 | 43 | /// 44 | /// AWS traces (OpenTelemetry.Instrumentation.AWS) 45 | /// 46 | AWS, 47 | 48 | /// 49 | /// AWS Lambda traces (OpenTelemetry.Instrumentation.AWSLambda) 50 | /// 51 | AWSLambda, 52 | 53 | /// 54 | /// ASP.NET metrics and traces (OpenTelemetry.Instrumentation.AspNetCore) 55 | /// 56 | AspNet, 57 | 58 | /// 59 | /// Cassandra metrics (OpenTelemetry.Instrumentation.Cassandra) 60 | /// 61 | Cassandra, 62 | 63 | /// 64 | /// Elasticsearch client traces (OpenTelemetry.Instrumentation.ElasticsearchClient) 65 | /// 66 | ElasticsearchClient, 67 | 68 | /// 69 | /// EntityFrameworkCore traces (OpenTelemetry.Instrumentation.EntityFrameworkCore) 70 | /// 71 | EntityFrameworkCore, 72 | 73 | /// 74 | /// Hangfire traces (OpenTelemetry.Instrumentation.Hangfire) 75 | /// 76 | Hangfire, 77 | 78 | /// 79 | /// MySqlData traces (OpenTelemetry.Instrumentation.MySqlData) 80 | /// 81 | MySqlData, 82 | 83 | /// 84 | /// Owin metrics and traces (OpenTelemetry.Instrumentation.Owin) 85 | /// 86 | Owin, 87 | 88 | /// 89 | /// Quarty traces (OpenTelemetry.Instrumentation.Quartz) 90 | /// 91 | Quartz, 92 | 93 | /// 94 | /// StackExchange.Redis traces (OpenTelemetry.Instrumentation.StackExchangeRedis) 95 | /// 96 | StackExchangeRedis, 97 | 98 | /// 99 | /// WCF traces (OpenTelemetry.Instrumentation.Wcf) 100 | /// 101 | Wcf 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/InstrumentationInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using OpenTelemetry.Metrics; 8 | using OpenTelemetry.Trace; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal abstract class InstrumentationInitializer 13 | { 14 | public static InstrumentationInitializer[] Initializers = new InstrumentationInitializer[] 15 | { 16 | #if NETFRAMEWORK 17 | new AspNetInitializer(), 18 | new OwinInitializer(), 19 | #endif 20 | new AspNetCoreInitializer(), 21 | new AWSInitializer(), 22 | new AWSLambdaInitializer(), 23 | new CassandraInitializer(), 24 | new ElasticsearchClientInitializer(), 25 | new EntityFrameworkCoreInitializer(), 26 | new GrpcNetClientInitializer(), 27 | new HangfireInitializer(), 28 | new HttpClientInitializer(), 29 | new MySqlDataInitializer(), 30 | new NetRuntimeMetricsInitializer(), 31 | new ProcessMetricsInitializer(), 32 | new QuartzInitializer(), 33 | new SqlClientInitializer(), 34 | new StackExchangeRedisInitializer(), 35 | new WcfInitializer(), 36 | }; 37 | 38 | abstract public Instrumentation Id { get; } 39 | 40 | public void Initialize(TracerProviderBuilder builder) 41 | { 42 | try 43 | { 44 | InitializeTracing(builder); 45 | 46 | GrafanaOpenTelemetryEventSource.Log.EnabledTracingInstrumentation(Id.ToString()); 47 | } 48 | catch (Exception ex) 49 | { 50 | GrafanaOpenTelemetryEventSource.Log.FailureEnablingTracingInstrumentation(Id.ToString(), ex); 51 | } 52 | } 53 | 54 | public void Initialize(MeterProviderBuilder builder) 55 | { 56 | try 57 | { 58 | InitializeMetrics(builder); 59 | 60 | GrafanaOpenTelemetryEventSource.Log.EnabledMetricsInstrumentation(Id.ToString()); 61 | } 62 | catch (Exception ex) 63 | { 64 | GrafanaOpenTelemetryEventSource.Log.FailureEnablingMetricsInstrumentation(Id.ToString(), ex); 65 | } 66 | } 67 | 68 | protected virtual void InitializeTracing(TracerProviderBuilder builder) 69 | { } 70 | 71 | protected virtual void InitializeMetrics(MeterProviderBuilder builder) 72 | { } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/MySqlDataInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class MySqlDataInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.MySqlData; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | // MySQL.Data.OpenTelemetry 17 | ReflectionHelper.CallStaticMethod( 18 | "MySQL.Data.OpenTelemetry", 19 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 20 | "AddConnectorNet", 21 | new object[] { builder }); 22 | 23 | // OpenTelemetry.Instrumentation.MySqlData 24 | ReflectionHelper.CallStaticMethod( 25 | "OpenTelemetry.Instrumentation.MySqlData", 26 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 27 | "AddMySqlDataInstrumentation", 28 | new object[] { builder }); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/NetRuntimeMetricsInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class NetRuntimeMetricsInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.NetRuntime; 13 | 14 | protected override void InitializeMetrics(MeterProviderBuilder builder) 15 | { 16 | builder.AddRuntimeInstrumentation(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/OwinInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class OwinInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Owin; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.Owin", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddOwinInstrumentation", 20 | new object[] { builder, null }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/ProcessMetricsInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Metrics; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class ProcessMetricsInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Process; 13 | 14 | protected override void InitializeMetrics(MeterProviderBuilder builder) 15 | { 16 | builder.AddProcessInstrumentation(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/QuartzInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class QuartzInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Quartz; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.Quartz", 18 | "OpenTelemetry.Trace.TraceProviderBuilderExtensions", 19 | "AddQuartzInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/SqlClientInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class SqlClientInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.SqlClient; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | builder.AddSqlClientInstrumentation(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/StackExchangeRedisInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class StackExchangeRedisInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.StackExchangeRedis; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.StackExchangeRedis", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddRedisInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/Instrumentations/WcfInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Trace; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class WcfInitializer : InstrumentationInitializer 11 | { 12 | public override Instrumentation Id { get; } = Instrumentation.Wcf; 13 | 14 | protected override void InitializeTracing(TracerProviderBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Instrumentation.Wcf", 18 | "OpenTelemetry.Trace.TracerProviderBuilderExtensions", 19 | "AddWcfInstrumentation", 20 | new object[] { builder }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/MeterProviderBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using OpenTelemetry.Metrics; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | /// 13 | /// Metrics-related extension provided by the OpenTelemetry .NET distribution for Grafana. 14 | /// 15 | public static class MeterProviderBuilderExtensions 16 | { 17 | /// 18 | /// Sets up metrics with the OpenTelemetry .NET distribution for Grafana. 19 | /// 20 | /// A 21 | /// A callback for customizing default Grafana OpenTelemetry settings 22 | /// A modified 23 | public static MeterProviderBuilder UseGrafana(this MeterProviderBuilder builder, Action configure = default) 24 | { 25 | GrafanaOpenTelemetrySettings settings = new GrafanaOpenTelemetrySettings(); 26 | 27 | if (configure != null) 28 | { 29 | configure?.Invoke(settings); 30 | } 31 | 32 | GrafanaOpenTelemetryEventSource.Log.InitializeDistribution(settings); 33 | 34 | // Default to using experimental gRPC instrumentation 35 | if (Environment.GetEnvironmentVariable("OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION") == null) 36 | { 37 | Environment.SetEnvironmentVariable("OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION", "true"); 38 | } 39 | 40 | return builder 41 | .AddGrafanaExporter(settings?.ExporterSettings) 42 | .AddInstrumentations(settings?.Instrumentations) 43 | .AddResourceDetectors(settings?.ResourceDetectors) 44 | .ConfigureResource(resourceBuilder => resourceBuilder.AddGrafanaResource(settings)); 45 | } 46 | 47 | internal static MeterProviderBuilder AddGrafanaExporter(this MeterProviderBuilder builder, ExporterSettings settings) 48 | { 49 | settings?.Apply(builder); 50 | 51 | return builder; 52 | } 53 | 54 | internal static MeterProviderBuilder AddInstrumentations(this MeterProviderBuilder builder, HashSet instrumentations) 55 | { 56 | if (instrumentations == null) 57 | { 58 | return builder; 59 | } 60 | 61 | foreach (var initializer in InstrumentationInitializer.Initializers) 62 | { 63 | if (instrumentations.Contains(initializer.Id)) 64 | { 65 | initializer.Initialize(builder); 66 | } 67 | } 68 | 69 | return builder; 70 | } 71 | 72 | internal static MeterProviderBuilder AddResourceDetectors(this MeterProviderBuilder builder, HashSet resourceDetectors) 73 | { 74 | if (resourceDetectors == null) 75 | { 76 | return builder; 77 | } 78 | 79 | foreach (var initializer in ResourceDetectorInitializer.Initializers) 80 | { 81 | if (resourceDetectors.Contains(initializer.Id)) 82 | { 83 | initializer.Initialize(builder); 84 | } 85 | } 86 | 87 | return builder; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/OpenTelemetryLoggerOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using Microsoft.Extensions.Logging; 8 | using OpenTelemetry.Logs; 9 | using OpenTelemetry.Resources; 10 | 11 | namespace Grafana.OpenTelemetry 12 | { 13 | /// 14 | /// Logging-related extension provided by the OpenTelemetry .NET distribution for Grafana. 15 | /// 16 | public static class OpenTelemetryLoggerOptionsExtensions 17 | { 18 | /// 19 | /// Sets up an with the OpenTelemetry .NET distribution for Grafana. 20 | /// 21 | /// 22 | /// A callback for customizing default Grafana OpenTelemetry settings 23 | /// A modified object 24 | public static OpenTelemetryLoggerOptions UseGrafana(this OpenTelemetryLoggerOptions options, Action configure = default) 25 | { 26 | GrafanaOpenTelemetrySettings settings = new GrafanaOpenTelemetrySettings(); 27 | 28 | if (configure != null) 29 | { 30 | configure?.Invoke(settings); 31 | } 32 | 33 | GrafanaOpenTelemetryEventSource.Log.InitializeDistribution(settings); 34 | 35 | // Default to using stable HTTP semantic conventions 36 | if (Environment.GetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN") == null) 37 | { 38 | Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", "http"); 39 | } 40 | 41 | var resourceBuilder = ResourceBuilder 42 | .CreateDefault() 43 | .AddGrafanaResource(settings); 44 | 45 | return options 46 | .AddGrafanaExporter(settings?.ExporterSettings) 47 | .SetResourceBuilder(resourceBuilder); 48 | } 49 | 50 | internal static OpenTelemetryLoggerOptions AddGrafanaExporter(this OpenTelemetryLoggerOptions options, ExporterSettings settings) 51 | { 52 | settings?.Apply(options); 53 | 54 | return options; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal static class ReflectionHelper 12 | { 13 | internal static void CallStaticMethod(string assemblyName, string typeName, string methodName, object[] arguments) 14 | { 15 | var assembly = Assembly.Load(assemblyName); 16 | var type = assembly.GetType(typeName); 17 | var method = type.GetMethod(methodName, arguments.Select(obj => obj is null ? null : obj.GetType()).ToArray()); 18 | method.Invoke(null, arguments); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Resources; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal static class ResourceBuilderExtension 11 | { 12 | public static ResourceBuilder AddGrafanaResource(this ResourceBuilder resourceBuilder, GrafanaOpenTelemetrySettings settings) 13 | { 14 | var serviceInstanceIdProvided = !string.IsNullOrEmpty(settings.ServiceInstanceId); 15 | 16 | return resourceBuilder 17 | .AddDetector(new GrafanaOpenTelemetryResourceDetector(settings)) 18 | .AddService( 19 | serviceName: settings.ServiceName, 20 | serviceVersion: settings.ServiceVersion, 21 | serviceInstanceId: serviceInstanceIdProvided ? settings.ServiceInstanceId : null, 22 | autoGenerateServiceInstanceId: serviceInstanceIdProvided == false); 23 | 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AWSEBSDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | using OpenTelemetry.Resources; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AWSEBSDetectorInitializer : ResourceDetectorInitializer 12 | { 13 | public override ResourceDetector Id { get; } = ResourceDetector.AWSEBS; 14 | 15 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Resources.AWS", 19 | "OpenTelemetry.Resources.AWSResourceBuilderExtensions", 20 | "AddAWSEBSDetector", 21 | new object[] { builder }); 22 | return builder; 23 | } 24 | } 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AWSEC2DetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | using OpenTelemetry.Resources; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AWSEC2DetectorInitializer : ResourceDetectorInitializer 12 | { 13 | public override ResourceDetector Id { get; } = ResourceDetector.AWSEC2; 14 | 15 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Resources.AWS", 19 | "OpenTelemetry.Resources.AWSResourceBuilderExtensions", 20 | "AddAWSEC2Detector", 21 | new object[] { builder }); 22 | return builder; 23 | } 24 | } 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AWSECSDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD && !NETFRAMEWORK 7 | using OpenTelemetry.Resources; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AWSECSDetectorInitializer : ResourceDetectorInitializer 12 | { 13 | public override ResourceDetector Id { get; } = ResourceDetector.AWSECS; 14 | 15 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Resources.AWS", 19 | "OpenTelemetry.Resources.AWSResourceBuilderExtensions", 20 | "AddAWSECSDetector", 21 | new object[] { builder }); 22 | return builder; 23 | } 24 | } 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AWSEKSDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD && !NETFRAMEWORK 7 | using OpenTelemetry.Resources; 8 | 9 | namespace Grafana.OpenTelemetry 10 | { 11 | internal class AWSEKSDetectorInitializer : ResourceDetectorInitializer 12 | { 13 | public override ResourceDetector Id { get; } = ResourceDetector.AWSEKS; 14 | 15 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 16 | { 17 | ReflectionHelper.CallStaticMethod( 18 | "OpenTelemetry.Resources.AWS", 19 | "OpenTelemetry.Resources.AWSResourceBuilderExtensions", 20 | "AddAWSEKSDetector", 21 | new object[] { builder }); 22 | return builder; 23 | } 24 | } 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AzureAppServiceDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Resources; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class AzureAppServiceDetectorInitializer : ResourceDetectorInitializer 11 | { 12 | public override ResourceDetector Id { get; } = ResourceDetector.AzureAppService; 13 | 14 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Resources.Azure", 18 | "OpenTelemetry.Resources.AzureResourceBuilderExtensions", 19 | "AddAzureAppServiceDetector", 20 | new object[] { builder }); 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AzureContainerAppsDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Resources; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class AzureContainerAppsDetectorInitializer : ResourceDetectorInitializer 11 | { 12 | public override ResourceDetector Id { get; } = ResourceDetector.AzureContainerApps; 13 | 14 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Resources.Azure", 18 | "OpenTelemetry.Resources.AzureResourceBuilderExtensions", 19 | "AddAzureContainerAppsDetector", 20 | new object[] { builder }); 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/AzureVMDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry.Resources; 7 | 8 | namespace Grafana.OpenTelemetry 9 | { 10 | internal class AzureVMDetectorInitializer : ResourceDetectorInitializer 11 | { 12 | public override ResourceDetector Id { get; } = ResourceDetector.AzureVM; 13 | 14 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 15 | { 16 | ReflectionHelper.CallStaticMethod( 17 | "OpenTelemetry.Resources.Azure", 18 | "OpenTelemetry.Resources.AzureResourceBuilderExtensions", 19 | "AddAzureVMDetector", 20 | new object[] { builder }); 21 | return builder; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/ContainerResource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if NET8_0_OR_GREATER 7 | 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class ContainerResourceInitializer : ResourceDetectorInitializer 13 | { 14 | public override ResourceDetector Id { get; } = ResourceDetector.Container; 15 | 16 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 17 | { 18 | return builder.AddContainerDetector(); 19 | } 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/HostDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class HostResourceInitializer : ResourceDetectorInitializer 13 | { 14 | public override ResourceDetector Id { get; } = ResourceDetector.Host; 15 | 16 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 17 | { 18 | return builder.AddHostDetector(); 19 | } 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/OperatingSystemResourceInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class OperatingSystemResourceInitializer : ResourceDetectorInitializer 13 | { 14 | public override ResourceDetector Id { get; } = ResourceDetector.OperatingSystem; 15 | 16 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 17 | { 18 | return builder.AddOperatingSystemDetector(); 19 | } 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/ProcessResourceInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class ProcessResourceInitializer : ResourceDetectorInitializer 13 | { 14 | public override ResourceDetector Id { get; } = ResourceDetector.Process; 15 | 16 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 17 | { 18 | return builder.AddProcessDetector(); 19 | } 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/ProcessRuntimeResource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETSTANDARD 7 | 8 | using OpenTelemetry.Resources; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | internal class ProcessRuntimeResourceInitializer : ResourceDetectorInitializer 13 | { 14 | public override ResourceDetector Id { get; } = ResourceDetector.ProcessRuntime; 15 | 16 | protected override ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 17 | { 18 | return builder.AddProcessRuntimeDetector(); 19 | } 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/ResourceDetector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | namespace Grafana.OpenTelemetry 7 | { 8 | /// 9 | /// An enum representing different resource detectors. 10 | /// 11 | public enum ResourceDetector 12 | { 13 | /// 14 | /// AWS EBS Resource Detector (OpenTelemetry.Resources.AWS) 15 | /// 16 | AWSEBS, 17 | /// 18 | /// AWS EC2 Resource Detector (OpenTelemetry.Resources.AWS) 19 | /// 20 | AWSEC2, 21 | /// 22 | /// AWS ECS Resource Detector (OpenTelemetry.Resources.AWS) 23 | /// 24 | AWSECS, 25 | /// 26 | /// AWS EKS Resource Detector (OpenTelemetry.Resources.AWS) 27 | /// 28 | AWSEKS, 29 | 30 | /// 31 | /// Azure App Service Resource Detector (OpenTelemetry.Resources.Azure) 32 | /// 33 | AzureAppService, 34 | /// 35 | /// Azure Virtual Machine Resource Detector (OpenTelemetry.Resources.Azure) 36 | /// 37 | AzureVM, 38 | /// 39 | /// Azure Container Apps Resource Detector (OpenTelemetry.Resources.Azure) 40 | /// 41 | AzureContainerApps, 42 | 43 | /// 44 | /// Container Resource Detector (OpenTelemetry.Resources.Container) 45 | /// 46 | Container, 47 | 48 | /// 49 | /// Host Resource Detector (OpenTelemetry.Resources.Host) 50 | /// 51 | Host, 52 | 53 | /// 54 | /// Operating System Resource Detector (OpenTelemetry.Resources.OperatingSystem) 55 | /// 56 | OperatingSystem, 57 | 58 | /// 59 | /// Process Resource Detector (OpenTelemetry.Resources.Process) 60 | /// 61 | Process, 62 | 63 | /// 64 | /// Process Runtime Resource Detector (OpenTelemetry.Resources.ProcessRuntime) 65 | /// 66 | ProcessRuntime 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/ResourceDetectors/ResourceDetectorInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using OpenTelemetry.Metrics; 8 | using OpenTelemetry.Resources; 9 | using OpenTelemetry.Trace; 10 | 11 | namespace Grafana.OpenTelemetry 12 | { 13 | internal abstract class ResourceDetectorInitializer 14 | { 15 | public static ResourceDetectorInitializer[] Initializers = new ResourceDetectorInitializer[] 16 | { 17 | #if !NETSTANDARD 18 | new AWSEBSDetectorInitializer(), 19 | new AWSEC2DetectorInitializer(), 20 | #endif 21 | #if !NETSTANDARD && !NETFRAMEWORK 22 | new AWSECSDetectorInitializer(), 23 | new AWSEKSDetectorInitializer(), 24 | #endif 25 | new AzureAppServiceDetectorInitializer(), 26 | new AzureVMDetectorInitializer(), 27 | new AzureContainerAppsDetectorInitializer(), 28 | #if NET8_0_OR_GREATER 29 | new ContainerResourceInitializer(), 30 | #endif 31 | #if !NETSTANDARD 32 | new HostResourceInitializer(), 33 | new OperatingSystemResourceInitializer(), 34 | new ProcessResourceInitializer(), 35 | new ProcessRuntimeResourceInitializer() 36 | #endif 37 | }; 38 | 39 | abstract public ResourceDetector Id { get; } 40 | 41 | public void Initialize(TracerProviderBuilder builder) 42 | { 43 | try 44 | { 45 | builder.ConfigureResource(resourceBuilder => InitializeResourceDetector(resourceBuilder)); 46 | 47 | GrafanaOpenTelemetryEventSource.Log.EnabledTracingInstrumentation(Id.ToString()); 48 | } 49 | catch (Exception ex) 50 | { 51 | GrafanaOpenTelemetryEventSource.Log.FailureEnablingTracingInstrumentation(Id.ToString(), ex); 52 | } 53 | } 54 | 55 | public void Initialize(MeterProviderBuilder builder) 56 | { 57 | try 58 | { 59 | builder.ConfigureResource(resourceBuilder => InitializeResourceDetector(resourceBuilder)); 60 | 61 | GrafanaOpenTelemetryEventSource.Log.EnabledMetricsInstrumentation(Id.ToString()); 62 | } 63 | catch (Exception ex) 64 | { 65 | GrafanaOpenTelemetryEventSource.Log.FailureEnablingMetricsInstrumentation(Id.ToString(), ex); 66 | } 67 | } 68 | 69 | protected virtual ResourceBuilder InitializeResourceDetector(ResourceBuilder builder) 70 | { 71 | return builder; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry.Base/TracerProviderBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using OpenTelemetry.Trace; 9 | 10 | namespace Grafana.OpenTelemetry 11 | { 12 | /// 13 | /// Tracing-related extension provided by the OpenTelemetry .NET distribution for Grafana. 14 | /// 15 | public static class TracerProviderBuilderExtensions 16 | { 17 | /// 18 | /// Sets up tracing with the OpenTelemetry .NET distribution for Grafana. 19 | /// 20 | /// A 21 | /// A callback for customizing default Grafana OpenTelemetry settings 22 | /// A modified 23 | public static TracerProviderBuilder UseGrafana(this TracerProviderBuilder builder, Action configure = default) 24 | { 25 | GrafanaOpenTelemetrySettings settings = new GrafanaOpenTelemetrySettings(); 26 | 27 | if (configure != null) 28 | { 29 | configure?.Invoke(settings); 30 | } 31 | 32 | GrafanaOpenTelemetryEventSource.Log.InitializeDistribution(settings); 33 | 34 | // Default to using experimental gRPC instrumentation 35 | if (Environment.GetEnvironmentVariable("OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION") == null) 36 | { 37 | Environment.SetEnvironmentVariable("OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION", "true"); 38 | } 39 | 40 | return builder 41 | .AddGrafanaExporter(settings?.ExporterSettings) 42 | .AddInstrumentations(settings?.Instrumentations) 43 | .AddResourceDetectors(settings?.ResourceDetectors) 44 | .ConfigureResource(resourceBuilder => resourceBuilder.AddGrafanaResource(settings)); 45 | } 46 | 47 | internal static TracerProviderBuilder AddGrafanaExporter(this TracerProviderBuilder builder, ExporterSettings settings) 48 | { 49 | settings?.Apply(builder); 50 | 51 | return builder; 52 | } 53 | 54 | internal static TracerProviderBuilder AddInstrumentations(this TracerProviderBuilder builder, HashSet instrumentations) 55 | { 56 | if (instrumentations == null) 57 | { 58 | return builder; 59 | } 60 | 61 | foreach (var initializer in InstrumentationInitializer.Initializers) 62 | { 63 | if (instrumentations.Contains(initializer.Id)) 64 | { 65 | initializer.Initialize(builder); 66 | } 67 | } 68 | 69 | return builder; 70 | } 71 | 72 | internal static TracerProviderBuilder AddResourceDetectors(this TracerProviderBuilder builder, HashSet resourceDetectors) 73 | { 74 | if (resourceDetectors == null) 75 | { 76 | return builder; 77 | } 78 | 79 | foreach (var initializer in ResourceDetectorInitializer.Initializers) 80 | { 81 | if (resourceDetectors.Contains(initializer.Id)) 82 | { 83 | initializer.Initialize(builder); 84 | } 85 | } 86 | 87 | return builder; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry/Grafana.OpenTelemetry.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Full Grafana distribution of OpenTelemetry .NET 5 | true 6 | net8.0;netstandard2.0;net462 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | <_Parameter1>Grafana.OpenTelemetry.Tests 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry/OpenTelemetryBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | #if !NETFRAMEWORK 7 | 8 | using System; 9 | using OpenTelemetry; 10 | 11 | namespace Grafana.OpenTelemetry 12 | { 13 | /// 14 | /// Extension for the provided by the OpenTelemetry .NET distribution 15 | /// for Grafana. 16 | /// 17 | /// This is used for easier configuration for ASP.NET Core projects. 18 | /// 19 | public static class OpenTelemetryBuilderExtension 20 | { 21 | /// 22 | /// Sets up tracing and metrics with the OpenTelemetry .NET distribution for Grafana. 23 | /// 24 | /// A 25 | /// A callback for customizing default Grafana OpenTelemetry settings 26 | /// A modified 27 | public static IOpenTelemetryBuilder UseGrafana(this IOpenTelemetryBuilder builder, Action configure = default) 28 | { 29 | return builder 30 | .WithTracing(tracerProviderBuilder => tracerProviderBuilder.UseGrafana(configure)) 31 | .WithMetrics(metricProviderBuilder => metricProviderBuilder.UseGrafana(configure)); 32 | } 33 | } 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /src/Grafana.OpenTelemetry/rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/Grafana.OpenTelemetry.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | $(TargetFrameworks);net481 6 | false 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/GrafanaCloudConfigurationHelperTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using Xunit; 8 | 9 | namespace Grafana.OpenTelemetry.Tests 10 | { 11 | public class GrafanaCloudConfigurationHelperTest 12 | { 13 | [Fact] 14 | public void OtlpEndpoint() 15 | { 16 | var helper = new GrafanaCloudConfigurationHelper("prod-us-east-0", "", ""); 17 | 18 | Assert.Equal( 19 | new Uri($"https://otlp-gateway-prod-us-east-0.grafana.net/otlp/v1/traces"), 20 | helper.OtlpEndpointTraces); 21 | 22 | Assert.Equal( 23 | new Uri($"https://otlp-gateway-prod-us-east-0.grafana.net/otlp/v1/metrics"), 24 | helper.OtlpEndpointMetrics); 25 | 26 | Assert.Equal( 27 | new Uri($"https://otlp-gateway-prod-us-east-0.grafana.net/otlp/v1/logs"), 28 | helper.OtlpEndpointLogs); 29 | } 30 | 31 | [Fact] 32 | public void OtlpAuthorizationHeader() 33 | { 34 | var helper = new GrafanaCloudConfigurationHelper("", "701628", "a_secret"); 35 | 36 | Assert.Equal( 37 | "Authorization=Basic NzAxNjI4OmFfc2VjcmV0", 38 | helper.OtlpAuthorizationHeader); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/GrafanaOpenTelemetryResourceDetectorTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using Xunit; 9 | 10 | namespace Grafana.OpenTelemetry.Tests 11 | { 12 | public class GrafanaOpenTelemetryResourceDetectorTest 13 | { 14 | [Fact] 15 | public void DefaultDeploymentEnvironment() 16 | { 17 | var settings = new GrafanaOpenTelemetrySettings(); 18 | var resource = new GrafanaOpenTelemetryResourceDetector(settings).Detect(); 19 | var resourceAttributes = new Dictionary(); 20 | 21 | foreach (var attribute in resource.Attributes) 22 | { 23 | resourceAttributes[attribute.Key] = attribute.Value; 24 | } 25 | 26 | Assert.Equal("production", resourceAttributes["deployment.environment"]); 27 | } 28 | 29 | [Fact] 30 | public void CustomDeploymentEnvironment() 31 | { 32 | var settings = new GrafanaOpenTelemetrySettings(); 33 | settings.DeploymentEnvironment = "custom"; 34 | 35 | var resource = new GrafanaOpenTelemetryResourceDetector(settings).Detect(); 36 | var resourceAttributes = new Dictionary(); 37 | 38 | foreach (var attribute in resource.Attributes) 39 | { 40 | resourceAttributes[attribute.Key] = attribute.Value; 41 | } 42 | 43 | Assert.Equal("custom", resourceAttributes["deployment.environment"]); 44 | } 45 | 46 | [Fact] 47 | public void DeploymentEnvironmentFromEnv() 48 | { 49 | Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "CustomEnv"); 50 | 51 | var settings = new GrafanaOpenTelemetrySettings(); 52 | var resource = new GrafanaOpenTelemetryResourceDetector(settings).Detect(); 53 | var resourceAttributes = new Dictionary(); 54 | 55 | foreach (var attribute in resource.Attributes) 56 | { 57 | resourceAttributes[attribute.Key] = attribute.Value; 58 | } 59 | 60 | Assert.Equal("customenv", resourceAttributes["deployment.environment"]); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/GrafanaOpenTelemetrySettingsTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Reflection; 9 | using Xunit; 10 | 11 | namespace Grafana.OpenTelemetry.Tests 12 | { 13 | public class GrafanaOpenTelemetrySettingsTest 14 | { 15 | public GrafanaOpenTelemetrySettingsTest() 16 | { 17 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableInstrumentationsEnvVarName, null); 18 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ServiceNameEnvVarName, null); 19 | } 20 | 21 | [Fact(Skip = "provider builders crashes on enabling AWSLambda instrumentation by default")] 22 | public void DefaultInstrumentations() 23 | { 24 | var settings = new GrafanaOpenTelemetrySettings(); 25 | 26 | Assert.Equal(new HashSet((Instrumentation[])Enum.GetValues(typeof(Instrumentation))), settings.Instrumentations); 27 | } 28 | 29 | [Fact] 30 | public void DefaultInstrumentationsWithout() 31 | { 32 | var settings = new GrafanaOpenTelemetrySettings(); 33 | 34 | // De-activate AWSLambda instrumenation until this issue is resolved: https://github.com/grafana/app-o11y/issues/378 35 | // 36 | // Once it's resolved, this test can be removed and the `DefaultInstrumentation` test can be re-activated. 37 | var expectedSettings = new HashSet((Instrumentation[])Enum.GetValues(typeof(Instrumentation))); 38 | expectedSettings.Remove(Instrumentation.AWSLambda); 39 | 40 | Assert.Equal(expectedSettings, settings.Instrumentations); 41 | } 42 | 43 | [Fact] 44 | public void DisableInstrumentations() 45 | { 46 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableInstrumentationsEnvVarName, "Process,NetRuntime"); 47 | 48 | var settings = new GrafanaOpenTelemetrySettings(); 49 | 50 | Assert.DoesNotContain(Instrumentation.Process, settings.Instrumentations); 51 | Assert.DoesNotContain(Instrumentation.NetRuntime, settings.Instrumentations); 52 | 53 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableInstrumentationsEnvVarName, null); 54 | } 55 | 56 | [Fact] 57 | public void DisableInstrumentationsColon() 58 | { 59 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableInstrumentationsEnvVarName, "Process:NetRuntime"); 60 | 61 | var settings = new GrafanaOpenTelemetrySettings(); 62 | 63 | Assert.DoesNotContain(Instrumentation.Process, settings.Instrumentations); 64 | Assert.DoesNotContain(Instrumentation.NetRuntime, settings.Instrumentations); 65 | 66 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableInstrumentationsEnvVarName, null); 67 | } 68 | 69 | [Fact] 70 | public void DisableResourceDetectors() 71 | { 72 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, "Host,Process"); 73 | 74 | var settings = new GrafanaOpenTelemetrySettings(); 75 | 76 | Assert.DoesNotContain(ResourceDetector.Host, settings.ResourceDetectors); 77 | Assert.DoesNotContain(ResourceDetector.Process, settings.ResourceDetectors); 78 | 79 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, null); 80 | } 81 | 82 | [Fact] 83 | public void DisableResourceDetectorsColon() 84 | { 85 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, "Host:Process"); 86 | 87 | var settings = new GrafanaOpenTelemetrySettings(); 88 | 89 | Assert.DoesNotContain(ResourceDetector.Host, settings.ResourceDetectors); 90 | Assert.DoesNotContain(ResourceDetector.Process, settings.ResourceDetectors); 91 | 92 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, null); 93 | } 94 | 95 | [Fact] 96 | public void SetSingleResourceDetector() 97 | { 98 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, "Container"); 99 | 100 | var settings = new GrafanaOpenTelemetrySettings(); 101 | 102 | Assert.Single(settings.ResourceDetectors); 103 | Assert.Contains(ResourceDetector.Container, settings.ResourceDetectors); 104 | 105 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, null); 106 | } 107 | 108 | [Fact] 109 | public void SetResourceDetectors() 110 | { 111 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, "Container,Process"); 112 | 113 | var settings = new GrafanaOpenTelemetrySettings(); 114 | 115 | Assert.Equal(2, settings.ResourceDetectors.Count); 116 | Assert.Contains(ResourceDetector.Container, settings.ResourceDetectors); 117 | Assert.Contains(ResourceDetector.Process, settings.ResourceDetectors); 118 | 119 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, null); 120 | } 121 | 122 | [Fact] 123 | public void SetResourceDetectorsColon() 124 | { 125 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, "Container:Process"); 126 | 127 | var settings = new GrafanaOpenTelemetrySettings(); 128 | 129 | Assert.Equal(2, settings.ResourceDetectors.Count); 130 | Assert.Contains(ResourceDetector.Container, settings.ResourceDetectors); 131 | Assert.Contains(ResourceDetector.Process, settings.ResourceDetectors); 132 | 133 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, null); 134 | } 135 | 136 | [Fact] 137 | public void SetAndDisableResourceDetectors() 138 | { 139 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, "Host,Container"); 140 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, "Container"); 141 | 142 | var settings = new GrafanaOpenTelemetrySettings(); 143 | 144 | Assert.Single(settings.ResourceDetectors); 145 | Assert.Contains(ResourceDetector.Host, settings.ResourceDetectors); 146 | 147 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ResourceDetectorsEnvVarName, null); 148 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.DisableResourceDetectorsEnvVarName, null); 149 | } 150 | 151 | [Fact] 152 | public void DefaultServiceName() 153 | { 154 | var settings = new GrafanaOpenTelemetrySettings(); 155 | 156 | Assert.Equal(Assembly.GetEntryAssembly()?.GetName().Name ?? System.Diagnostics.Process.GetCurrentProcess().ProcessName, settings.ServiceName); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/InMemoryResourceExporter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using OpenTelemetry; 9 | using OpenTelemetry.Resources; 10 | 11 | namespace Grafana.OpenTelemetry.Tests 12 | { 13 | /// 14 | /// The upstream `InMemoryExporter` doesn't capture resources. 15 | /// 16 | /// This implementation adds support for capturing the resource. 17 | /// 18 | public class InMemoryResourceExporter : BaseExporter 19 | where T : class 20 | { 21 | private readonly ICollection<(T, Resource)> exportedItems; 22 | private readonly ExportFunc onExport; 23 | private bool disposed; 24 | private string disposedStackTrace; 25 | 26 | public InMemoryResourceExporter(ICollection<(T, Resource)> exportedItems) 27 | { 28 | this.exportedItems = exportedItems; 29 | this.onExport = this.DefaultExport; 30 | } 31 | 32 | private InMemoryResourceExporter(ExportFunc exportFunc) 33 | { 34 | this.onExport = exportFunc; 35 | } 36 | 37 | internal delegate ExportResult ExportFunc(in Batch batch); 38 | 39 | public override ExportResult Export(in Batch batch) 40 | { 41 | if (this.disposed) 42 | { 43 | // Note: In-memory exporter is designed for testing purposes so this error is strategic to surface lifecycle management bugs during development. 44 | throw new ObjectDisposedException( 45 | this.GetType().Name, 46 | $"The in-memory exporter is still being invoked after it has been disposed. This could be the result of invalid lifecycle management of the OpenTelemetry .NET SDK. Dispose was called on the following stack trace:{Environment.NewLine}{this.disposedStackTrace}"); 47 | } 48 | 49 | return this.onExport(batch); 50 | } 51 | 52 | /// 53 | protected override void Dispose(bool disposing) 54 | { 55 | if (!this.disposed) 56 | { 57 | this.disposedStackTrace = Environment.StackTrace; 58 | this.disposed = true; 59 | } 60 | 61 | base.Dispose(disposing); 62 | } 63 | 64 | private ExportResult DefaultExport(in Batch batch) 65 | { 66 | if (this.exportedItems == null) 67 | { 68 | return ExportResult.Failure; 69 | } 70 | 71 | foreach (var data in batch) 72 | { 73 | this.exportedItems.Add((data, this.ParentProvider.GetResource())); 74 | } 75 | 76 | return ExportResult.Success; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/InstrumentationTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using Xunit; 10 | 11 | namespace Grafana.OpenTelemetry.Tests 12 | { 13 | public class InstrumentationTest 14 | { 15 | [Fact] 16 | public void EnumMatchesInitializers() 17 | { 18 | var expected = new HashSet((Instrumentation[])Enum.GetValues(typeof(Instrumentation))); 19 | #if !NETFRAMEWORK 20 | expected.Remove(Instrumentation.AspNet); 21 | expected.Remove(Instrumentation.Owin); 22 | #endif 23 | var actual = new HashSet(InstrumentationInitializer.Initializers.Select(x => x.Id)); 24 | Assert.Equivalent(expected, actual, strict: true); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/MeterProviderExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using OpenTelemetry; 7 | using OpenTelemetry.Metrics; 8 | using Xunit; 9 | 10 | namespace Grafana.OpenTelemetry.Tests 11 | { 12 | public class MeterProviderExtensionsTest 13 | { 14 | [Fact] 15 | public void EnableDefaultInstrumentations() 16 | { 17 | Sdk 18 | .CreateMeterProviderBuilder() 19 | .UseGrafana() 20 | .Build(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/OpenTelemetryLoggerOptionsExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using Microsoft.Extensions.Logging; 7 | using Xunit; 8 | 9 | namespace Grafana.OpenTelemetry.Tests 10 | { 11 | public class OpenTelemetryLoggerOptionsExtensionsTest 12 | { 13 | [Fact] 14 | public void BuildDefault() 15 | { 16 | var loggerFactory = LoggerFactory.Create(builder => 17 | { 18 | builder.AddOpenTelemetry(logging => 19 | { 20 | logging.UseGrafana(); 21 | }); 22 | }); 23 | 24 | var logger = loggerFactory.CreateLogger(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/ReflectionHelperTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using Xunit; 8 | 9 | namespace Grafana.OpenTelemetry.Tests 10 | { 11 | public class ReflectionHelperTest 12 | { 13 | internal static int Counter = 0; 14 | 15 | #pragma warning disable xUnit1013 // Allow public method that's not a [Theory] 16 | public static void Increment(int increment) 17 | { 18 | Counter += increment; 19 | } 20 | #pragma warning restore xUnit1013 // Allow public method that's not a [Theory] 21 | 22 | [Fact] 23 | public void CallStaticMethod() 24 | { 25 | ReflectionHelper.CallStaticMethod( 26 | typeof(ReflectionHelperTest).Assembly.GetName().Name, 27 | "Grafana.OpenTelemetry.Tests.ReflectionHelperTest", 28 | "Increment", 29 | new object[] { 4 }); 30 | 31 | Assert.Equal(4, Counter); 32 | } 33 | 34 | [Fact] 35 | public void CallStaticMethodThrow() 36 | { 37 | Assert.Throws(() => 38 | { 39 | ReflectionHelper.CallStaticMethod( 40 | typeof(ReflectionHelperTest).Assembly.GetName().Name, 41 | "Not-exist", 42 | "Not-exist", 43 | new object[] { 4 }); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/ResourceBuilderExtensionTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System.Linq; 7 | using OpenTelemetry.Resources; 8 | using Xunit; 9 | 10 | namespace Grafana.OpenTelemetry.Tests 11 | { 12 | public class ResourceBuilderExtensionTest 13 | { 14 | [Fact] 15 | public void StableServiceInstanceId() 16 | { 17 | var resource1 = ResourceBuilder 18 | .CreateEmpty() 19 | .AddGrafanaResource(new GrafanaOpenTelemetrySettings()) 20 | .Build() 21 | .Attributes 22 | .ToDictionary(x => x.Key, x => x.Value); 23 | var resource2 = ResourceBuilder 24 | .CreateEmpty() 25 | .AddGrafanaResource(new GrafanaOpenTelemetrySettings()) 26 | .Build() 27 | .Attributes 28 | .ToDictionary(x => x.Key, x => x.Value); 29 | 30 | Assert.NotNull(resource1["service.instance.id"]); 31 | Assert.NotNull(resource2["service.instance.id"]); 32 | Assert.Equal(resource1["service.instance.id"], resource2["service.instance.id"]); 33 | } 34 | 35 | [Fact] 36 | public void OverrideServiceInstanceId() 37 | { 38 | var settings = new GrafanaOpenTelemetrySettings 39 | { 40 | ServiceInstanceId = "test-id" 41 | }; 42 | var resource1 = ResourceBuilder 43 | .CreateEmpty() 44 | .AddGrafanaResource(settings) 45 | .Build() 46 | .Attributes 47 | .ToDictionary(x => x.Key, x => x.Value); 48 | 49 | Assert.NotNull(resource1["service.instance.id"]); 50 | Assert.Equal(resource1["service.instance.id"], "test-id"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/ResourceDetectorTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using Xunit; 10 | 11 | namespace Grafana.OpenTelemetry.Tests 12 | { 13 | public class ResourceDetectorTest 14 | { 15 | [Fact] 16 | public void EnumMatchesInitializers() 17 | { 18 | var expected = new HashSet((ResourceDetector[])Enum.GetValues(typeof(ResourceDetector))); 19 | #if NETSTANDARD 20 | expected.Remove(ResourceDetector.AWSEBS); 21 | expected.Remove(ResourceDetector.AWSEC2); 22 | #endif 23 | #if NETSTANDARD || NETFRAMEWORK 24 | expected.Remove(ResourceDetector.AWSECS); 25 | expected.Remove(ResourceDetector.AWSEKS); 26 | #endif 27 | #if !NET8_0_OR_GREATER 28 | expected.Remove(ResourceDetector.Container); 29 | #endif 30 | #if NETSTANDARD 31 | expected.Remove(ResourceDetector.Host); 32 | expected.Remove(ResourceDetector.OperatingSystem); 33 | expected.Remove(ResourceDetector.Process); 34 | expected.Remove(ResourceDetector.ProcessRuntime); 35 | #endif 36 | var actual = new HashSet(ResourceDetectorInitializer.Initializers.Select(x => x.Id)); 37 | Assert.Equivalent(expected, actual, strict: true); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Grafana.OpenTelemetry.Tests/TracerProviderExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright Grafana Labs 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using OpenTelemetry; 10 | using OpenTelemetry.Resources; 11 | using OpenTelemetry.Trace; 12 | using Xunit; 13 | 14 | namespace Grafana.OpenTelemetry.Tests 15 | { 16 | public class TracerProviderExtensionsTest 17 | { 18 | private static ActivitySource activitySource = new ActivitySource(typeof(TracerProviderExtensionsTest).Name); 19 | 20 | [Fact] 21 | public void EnableDefaultInstrumentations() 22 | { 23 | Sdk 24 | .CreateTracerProviderBuilder() 25 | .UseGrafana() 26 | .Build(); 27 | } 28 | 29 | [Fact] 30 | public void StandardResourceAttributes() 31 | { 32 | var spans = new List<(Activity, Resource)>(); 33 | 34 | Sdk 35 | .CreateTracerProviderBuilder() 36 | .UseGrafana() 37 | .AddProcessor(new SimpleActivityExportProcessor(new InMemoryResourceExporter(spans))) 38 | .AddSource(activitySource.Name) 39 | .Build(); 40 | 41 | var span = activitySource.StartActivity("root"); 42 | span.Stop(); 43 | span.Dispose(); 44 | 45 | var activity = Assert.Single(spans); 46 | 47 | var resourceTags = new Dictionary(); 48 | 49 | foreach (var tag in activity.Item2.Attributes) 50 | { 51 | resourceTags.Add(tag.Key, tag.Value); 52 | } 53 | 54 | Assert.Equal("grafana-opentelemetry-dotnet", (string)resourceTags["telemetry.distro.name"]); 55 | Assert.NotNull(resourceTags["telemetry.distro.version"]); 56 | 57 | Assert.NotNull(resourceTags["service.name"]); 58 | Assert.NotNull(resourceTags["service.instance.id"]); 59 | Assert.NotNull(resourceTags["service.version"]); 60 | 61 | Assert.NotNull(resourceTags["deployment.environment"]); 62 | 63 | Assert.NotNull(resourceTags["process.runtime.name"]); 64 | Assert.NotNull(resourceTags["process.runtime.description"]); 65 | Assert.NotNull(resourceTags["process.runtime.version"]); 66 | 67 | Assert.NotNull(resourceTags["process.pid"]); 68 | 69 | Assert.NotNull(resourceTags["host.name"]); 70 | 71 | Assert.NotNull(resourceTags["telemetry.sdk.name"]); 72 | Assert.NotNull(resourceTags["telemetry.sdk.language"]); 73 | Assert.NotNull(resourceTags["telemetry.sdk.version"]); 74 | } 75 | 76 | [Fact] 77 | public void CustomResourceAttributes() 78 | { 79 | var spans = new List<(Activity, Resource)>(); 80 | 81 | Sdk 82 | .CreateTracerProviderBuilder() 83 | .UseGrafana(settings => 84 | { 85 | settings.ServiceName = "service-name"; 86 | settings.ResourceAttributes["custom.attribute"] = "custom_value"; 87 | }) 88 | .AddProcessor(new SimpleActivityExportProcessor(new InMemoryResourceExporter(spans))) 89 | .AddSource(activitySource.Name) 90 | .Build(); 91 | 92 | var span = activitySource.StartActivity("root"); 93 | span.Stop(); 94 | span.Dispose(); 95 | 96 | var activity = Assert.Single(spans); 97 | 98 | var resourceTags = new Dictionary(); 99 | 100 | foreach (var tag in activity.Item2.Attributes) 101 | { 102 | if (tag.Value is string val) 103 | { 104 | resourceTags.Add(tag.Key, val); 105 | } 106 | } 107 | 108 | Assert.Equal("custom_value", resourceTags["custom.attribute"]); 109 | Assert.Equal("service-name", resourceTags["service.name"]); 110 | } 111 | 112 | [Fact] 113 | public void OverrideResourceAttributes() 114 | { 115 | var spans = new List<(Activity, Resource)>(); 116 | 117 | Sdk 118 | .CreateTracerProviderBuilder() 119 | .UseGrafana() 120 | .ConfigureResource(resourceBuilder => 121 | { 122 | resourceBuilder 123 | .AddService( 124 | serviceName: "a", 125 | serviceVersion: "0", 126 | serviceInstanceId: "b"); 127 | }) 128 | .AddProcessor(new SimpleActivityExportProcessor(new InMemoryResourceExporter(spans))) 129 | .AddSource(activitySource.Name) 130 | .Build(); 131 | 132 | var span = activitySource.StartActivity("root"); 133 | span.Stop(); 134 | span.Dispose(); 135 | 136 | var activity = Assert.Single(spans); 137 | 138 | var resourceTags = new Dictionary(); 139 | 140 | foreach (var tag in activity.Item2.Attributes) 141 | { 142 | if (tag.Value is string val) 143 | { 144 | resourceTags.Add(tag.Key, val); 145 | } 146 | } 147 | 148 | Assert.Equal("a", resourceTags["service.name"]); 149 | Assert.Equal("b", resourceTags["service.instance.id"]); 150 | Assert.Equal("0", resourceTags["service.version"]); 151 | } 152 | 153 | [Fact] 154 | public void OverrideResourceAttributesEnvironment() 155 | { 156 | Environment.SetEnvironmentVariable(GrafanaOpenTelemetrySettings.ServiceNameEnvVarName, "service-name"); 157 | 158 | var spans = new List<(Activity, Resource)>(); 159 | 160 | Sdk 161 | .CreateTracerProviderBuilder() 162 | .UseGrafana() 163 | .AddProcessor(new SimpleActivityExportProcessor(new InMemoryResourceExporter(spans))) 164 | .AddSource(activitySource.Name) 165 | .Build(); 166 | 167 | var span = activitySource.StartActivity("root"); 168 | span.Stop(); 169 | span.Dispose(); 170 | 171 | var activity = Assert.Single(spans); 172 | 173 | var resourceTags = new Dictionary(); 174 | 175 | foreach (var tag in activity.Item2.Attributes) 176 | { 177 | if (tag.Value is string val) 178 | { 179 | resourceTags.Add(tag.Key, val); 180 | } 181 | } 182 | 183 | Assert.Equal("service-name", resourceTags["service.name"]); 184 | } 185 | } 186 | } 187 | --------------------------------------------------------------------------------