diff --git a/.github/workflows/ci-concurrency.yml b/.github/workflows/ci-concurrency.yml index 2734a1df94..1a0a5bcbd0 100644 --- a/.github/workflows/ci-concurrency.yml +++ b/.github/workflows/ci-concurrency.yml @@ -35,7 +35,7 @@ jobs: - name: Publish Artifacts if: always() && !cancelled() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.os }}-${{ matrix.project }}-${{ matrix.version }}-coyoteoutput path: '**/*_CoyoteOutput.*' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1c65eae060..ee706de7d0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,7 +47,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # Command-line programs to run using the OS shell. # https://git.io/JvXDl @@ -61,4 +61,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/publish-packages-1.0.yml b/.github/workflows/publish-packages-1.0.yml index 41cad730f4..450cc39856 100644 --- a/.github/workflows/publish-packages-1.0.yml +++ b/.github/workflows/publish-packages-1.0.yml @@ -36,7 +36,7 @@ jobs: run: dotnet pack OpenTelemetry.proj --configuration Release --no-build --no-restore - name: Publish Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ github.ref_name }}-packages path: '**/bin/**/*.*nupkg' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 95287991b1..f5657aa9f5 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: stale-pr-message: 'This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or Pushing will instruct the bot to automatically remove the label. This bot runs once per day.' close-pr-message: 'Closed as inactive. Feel free to reopen if this PR is still being worked on.' diff --git a/Directory.Packages.props b/Directory.Packages.props index a53d8cf720..8b4287b890 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ true - 1.6.0 + 1.7.0 + $(NoWarn);OTEL1000;OTEL1001;OTEL1002 diff --git a/build/RELEASING.md b/build/RELEASING.md index 77b94aaf42..9d942f92dc 100644 --- a/build/RELEASING.md +++ b/build/RELEASING.md @@ -84,7 +84,16 @@ Only for Maintainers. git push origin 1.0.0-rc9.7 ``` - If releasing both, push both tags above. + If releasing only a particular non-core component which has a dedicated + MinverTagPrefix such as AspNetCore instrumentation, only add and push the + tag with that particular prefix. For example: + + ```sh + git tag -a Instrumentation.AspNetCore-1.6.0 -m "1.6.0 of AspNetCore instrumentation library" + git push origin Instrumentation.AspNetCore-1.6.0 + ``` + + If releasing multiple kinds of components, push both tags for each of them. 7. Go to the [list of tags](https://github.com/open-telemetry/opentelemetry-dotnet/tags) diff --git a/docs/diagnostics/README.md b/docs/diagnostics/README.md new file mode 100644 index 0000000000..2c959a6278 --- /dev/null +++ b/docs/diagnostics/README.md @@ -0,0 +1,23 @@ +# OpenTelemetry Diagnostics + +This document describes the diagnostic categories used in OpenTelemetry .NET +components. Diagnostics are used by the compiler to report information to users +about experimental and/or obsolete code being invoked or to suggest improvements +to specific code patterns identified through static analysis. + +## Experimental APIs + +Range: OTEL1XXX + +Experimental APIs exposed in OpenTelemetry .NET pre-relase builds. APIs are +exposed experimentally when either the OpenTelemetry Specification has +explicitly marked some feature as +[experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md) +or when the SIG members are still working through the design for a feature and +want to solicit feedback from the community. + +> **Note** Experimental APIs are exposed as `public` in pre-release builds and +> `internal` in stable builds. + +For defined diagnostics see: [OpenTelemetry .NET Experimental +APIs](./experimental-apis/README.md) diff --git a/docs/diagnostics/experimental-apis/OTEL1000.md b/docs/diagnostics/experimental-apis/OTEL1000.md new file mode 100644 index 0000000000..1ea4be023a --- /dev/null +++ b/docs/diagnostics/experimental-apis/OTEL1000.md @@ -0,0 +1,42 @@ +# OpenTelemetry .NET Diagnostic: OTEL1000 + +## Overview + +This is an Experimental API diagnostic covering the following APIs: + +* `LoggerProviderBuilder` +* `LoggerProvider` +* `IDeferredLoggerProviderBuilder` + +Experimental APIs may be changed or removed in the future. + +## Details + +The OpenTelemetry Specification defines a `LoggerProvider` as part of its +[API](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/bridge-api.md) +& +[SDK](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/sdk.md) +components. + +The SDK allows calling `Shutdown` and `ForceFlush` on the `LoggerProvider` and +also allows processors to be added dynamically to a pipeline after its creation. + +Today the OpenTelemetry .NET log pipeline is built on top of the +Microsoft.Extensions.Logging `ILogger` \ `ILoggerProvider` \ `ILoggerFactory` +APIs which do not expose such features. + +We also have an issue with the `ILoggingBuilder.AddOpenTelemetry` API in that it +interacts with the `OpenTelemetryLoggerOptions` class. Options classes are NOT +available until the `IServiceProvider` is available and services can no longer +be registered at that point. This prevents the current logging pipeline from +exposing the same dependency injection surface we have for traces and metrics. + +We are exposing these APIs to solve these issues and gather feedback about their +usefulness. + +## Logs Bridge API + +The OpenTelemetry Specification defines a Logs Bridge API which is rooted off of +the `LoggerProvider` (`GetLogger`) and exposes a `Logger` API to submit log +records. See [OTEL1001](./OTEL1001.md) for details about the Logs Bridge API +implementation status. diff --git a/docs/diagnostics/experimental-apis/OTEL1001.md b/docs/diagnostics/experimental-apis/OTEL1001.md new file mode 100644 index 0000000000..5386726e64 --- /dev/null +++ b/docs/diagnostics/experimental-apis/OTEL1001.md @@ -0,0 +1,35 @@ +# OpenTelemetry .NET Diagnostic: OTEL1001 + +## Overview + +This is an Experimental API diagnostic covering the following APIs: + +* `LoggerProvider.GetLogger` +* `Logger` +* `LogRecordAttributeList` +* `LogRecordData` +* `LogRecordSeverity` + +Experimental APIs may be changed or removed in the future. + +## Details + +The OpenTelemetry Specification defines a [Logs Bridge +API](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/bridge-api.md). + +The Logs Bridge API is used by library authors to build log appenders which +route messages from different log frameworks into OpenTelemetry. + +Today the OpenTelemetry .NET log pipeline is built on top of the +Microsoft.Extensions.Logging `ILogger` \ `ILoggerProvider` \ `ILoggerFactory` +APIs. + +We are exposing these APIs gather feedback about their usefulness. An +alternative approach may be taken which would be to append into `ILogger` +instead of OpenTelemetry directly. + +## LoggerProvider API + +The OpenTelemetry Specification defines a `LoggerProvider` API. See +[OTEL1000](./OTEL1000.md) for details about the `LoggerProvider` implementation +status. diff --git a/docs/diagnostics/experimental-apis/OTEL1002.md b/docs/diagnostics/experimental-apis/OTEL1002.md new file mode 100644 index 0000000000..8371f0d3bc --- /dev/null +++ b/docs/diagnostics/experimental-apis/OTEL1002.md @@ -0,0 +1,30 @@ +# OpenTelemetry .NET Diagnostic: OTEL1002 + +## Overview + +This is an Experimental API diagnostic covering the following APIs: + +* `AlwaysOnExemplarFilter` +* `AlwaysOffExemplarFilter` +* `Exemplar` +* `ExemplarFilter` +* `MeterProviderBuilder.SetExemplarFilter` extension method +* `TraceBasedExemplarFilter` + +Experimental APIs may be changed or removed in the future. + +## Details + +The OpenTelemetry Specification defines an [Exemplar +API](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplar) +in the Metrics SDK. + +From the specification: + +> Exemplars are example data points for aggregated data. They provide specific +> context to otherwise general aggregations. Exemplars allow correlation between +> aggregated metric data and the original API calls where measurements are +> recorded. + +We are exposing these APIs experimentally until the specification declares them +stable. diff --git a/docs/diagnostics/experimental-apis/README.md b/docs/diagnostics/experimental-apis/README.md new file mode 100644 index 0000000000..71a2d418fe --- /dev/null +++ b/docs/diagnostics/experimental-apis/README.md @@ -0,0 +1,48 @@ +# OpenTelemetry .NET Experimental APIs + +This document describes experimental APIs exposed in OpenTelemetry .NET +pre-relase builds. APIs are exposed experimentally when either the OpenTelemetry +Specification has explicitly marked some feature as +[experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md) +or when the SIG members are still working through the design for a feature and +want to solicit feedback from the community. + +> **Note** +> Experimental APIs are exposed as `public` in pre-release builds and `internal` +> in stable builds. + +## Active + +Experimental APIs available in the pre-release builds: + +### OTEL1000 + +Description: `LoggerProvider` and `LoggerProviderBuilder` + +Details: [OTEL1000](./OTEL1000.md) + +### OTEL1001 + +Description: Logs Bridge API + +Details: [OTEL1001](./OTEL1001.md) + +### OTEL1002 + +Description: Metrics Exemplar Support + +Details: [OTEL1002](./OTEL1002.md) + +## Inactive + +Experimental APIs which have been released stable or removed: + + + +None diff --git a/docs/metrics/exemplars/docker-compose.yaml b/docs/metrics/exemplars/docker-compose.yaml index 87cd7a6c6d..c8cc94fa4b 100644 --- a/docs/metrics/exemplars/docker-compose.yaml +++ b/docs/metrics/exemplars/docker-compose.yaml @@ -48,4 +48,3 @@ services: - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor ports: - "3000:3000" - diff --git a/docs/trace/README.md b/docs/trace/README.md new file mode 100644 index 0000000000..e9925cf656 --- /dev/null +++ b/docs/trace/README.md @@ -0,0 +1,49 @@ +# OpenTelemetry .NET Traces + +## Best Practices + +### ActivitySource singleton + +`ActivitySource` SHOULD only be created once and reused throughout the +application lifetime. This +[example](./getting-started-console/Program.cs) shows how +`ActivitySource` is created as a `static` field and then used in the +application. You could also look at this ASP.NET Core +[example](../../examples/AspNetCore/Program.cs) which shows a more Dependency +Injection friendly way of doing this by extracting the `ActivitySource` into a +dedicated class called +[Instrumentation](../../examples/AspNetCore/Instrumentation.cs) which is then +added as a `Singleton` service. + +### Manually creating Activities + +As shown in the [getting started](getting-started-console/README.md) guide, it +is very easy to manually create `Activity`. Due to this, it can be tempting to +create too many activities (eg: for each method call). In addition to being +expensive, excessive activities can also make trace visualization harder. +Instead of manually creating `Activity`, check if you can leverage +instrumentation libraries, such as [ASP.NET +Core](../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md), +[HttpClient](../../src/OpenTelemetry.Instrumentation.Http/README.md) which will +not only create and populate `Activity` with tags(attributes), but also take +care of propagating/restoring the context across process boundaries. If the +`Activity` produced by the instrumentation library is missing some information +you need, it is generally recommended to enrich the existing Activity with that +information, as opposed to creating a new one. + +## Common issues that lead to missing traces + +- The `ActivitySource` used to create the `Activity` is not added to the + `TracerProvider`. Use `AddSource` method to enable the activity from a given + `ActivitySource`. +- `TracerProvider` is disposed too early. You need to ensure that the + `TracerProvider` instance is kept active for traces to be collected. In a + typical application, a single TracerProvider is built at application startup, + and is disposed of at application shutdown. For an ASP.NET Core application, + use `AddOpenTelemetry` and `WithTraces` methods from the + `OpenTelemetry.Extensions.Hosting` package to correctly setup + `TracerProvider`. Here's a [sample ASP.NET Core + app](../../examples/AspNetCore/Program.cs) for reference. For simpler + applications such as Console apps, refer to this + [example](../../docs/trace/getting-started-console/Program.cs). +- TODO: Sampling diff --git a/examples/AspNetCore/Controllers/WeatherForecastController.cs b/examples/AspNetCore/Controllers/WeatherForecastController.cs index 5cd6f2cfe8..3d09fe9ed6 100644 --- a/examples/AspNetCore/Controllers/WeatherForecastController.cs +++ b/examples/AspNetCore/Controllers/WeatherForecastController.cs @@ -49,7 +49,7 @@ public IEnumerable Get() // that calculating the forecast is an expensive operation and therefore // something to be distinguished from the overall request. // Note: Tags can be added to the current activity without the need for - // a manual activity using Acitivty.Current?.SetTag() + // a manual activity using Activity.Current?.SetTag() using var activity = this.activitySource.StartActivity("calculate forecast"); var rng = new Random(); diff --git a/examples/AspNetCore/Instrumentation.cs b/examples/AspNetCore/Instrumentation.cs index 190a7d24a6..4b0ede1157 100644 --- a/examples/AspNetCore/Instrumentation.cs +++ b/examples/AspNetCore/Instrumentation.cs @@ -22,7 +22,7 @@ public Instrumentation() string? version = typeof(Instrumentation).Assembly.GetName().Version?.ToString(); this.ActivitySource = new ActivitySource(ActivitySourceName, version); this.meter = new Meter(MeterName, version); - this.FreezingDaysCounter = this.meter.CreateCounter("weather.days.freezing", "The number of days where the temperature is below freezing"); + this.FreezingDaysCounter = this.meter.CreateCounter("weather.days.freezing", description: "The number of days where the temperature is below freezing"); } public ActivitySource ActivitySource { get; } diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md b/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md index 12cac97046..25df4b8179 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs index 72b3f21de7..257b7332be 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs @@ -14,6 +14,9 @@ namespace OpenTelemetry.Logs; /// Contains extension methods for the class. /// #if EXPOSE_EXPERIMENTAL_FEATURES +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else internal diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggingServiceCollectionExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggingServiceCollectionExtensions.cs index b30085b574..8e6f899b24 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggingServiceCollectionExtensions.cs +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggingServiceCollectionExtensions.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Internal; @@ -10,6 +13,9 @@ namespace OpenTelemetry.Logs; /// Extension methods for setting up OpenTelemetry logging services in an . /// #if EXPOSE_EXPERIMENTAL_FEATURES +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else internal diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/IMeterProviderBuilder.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/IMeterProviderBuilder.cs index e52858935f..a7f2bf609f 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/IMeterProviderBuilder.cs +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/IMeterProviderBuilder.cs @@ -23,10 +23,10 @@ internal interface IMeterProviderBuilder : IDeferredMeterProviderBuilder /// /// Register a callback action to configure the where metric services are configured. + /// cref="IServiceCollection"/> where metrics services are configured. /// /// - /// Note: Metric services are only available during the application + /// Note: Metrics services are only available during the application /// configuration phase. This method should throw a if services are configured after the /// application has been created. diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs index 04ac51efe6..457ddc8766 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Metrics/OpenTelemetryDependencyInjectionMeterProviderBuilderExtensions.cs @@ -112,10 +112,10 @@ public static MeterProviderBuilder AddInstrumentation( /// /// Register a callback action to configure the where tracing services are configured. + /// cref="IServiceCollection"/> where metrics services are configured. /// /// - /// Note: Tracing services are only available during the application + /// Note: Metrics services are only available during the application /// configuration phase. /// /// . diff --git a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt index 6599b61330..a87bb70121 100644 --- a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Shipped.txt @@ -2,11 +2,6 @@ ~abstract OpenTelemetry.Context.Propagation.TextMapPropagator.Extract(OpenTelemetry.Context.Propagation.PropagationContext context, T carrier, System.Func> getter) -> OpenTelemetry.Context.Propagation.PropagationContext ~abstract OpenTelemetry.Context.Propagation.TextMapPropagator.Fields.get -> System.Collections.Generic.ISet ~abstract OpenTelemetry.Context.Propagation.TextMapPropagator.Inject(OpenTelemetry.Context.Propagation.PropagationContext context, T carrier, System.Action setter) -> void -abstract OpenTelemetry.Metrics.MeterProviderBuilder.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Metrics.MeterProviderBuilder! -abstract OpenTelemetry.Metrics.MeterProviderBuilder.AddMeter(params string![]! names) -> OpenTelemetry.Metrics.MeterProviderBuilder! -abstract OpenTelemetry.Trace.TracerProviderBuilder.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Trace.TracerProviderBuilder! -abstract OpenTelemetry.Trace.TracerProviderBuilder.AddLegacySource(string! operationName) -> OpenTelemetry.Trace.TracerProviderBuilder! -abstract OpenTelemetry.Trace.TracerProviderBuilder.AddSource(params string![]! names) -> OpenTelemetry.Trace.TracerProviderBuilder! ~OpenTelemetry.Baggage.GetBaggage() -> System.Collections.Generic.IReadOnlyDictionary ~OpenTelemetry.Baggage.GetBaggage(string name) -> string ~OpenTelemetry.Baggage.GetEnumerator() -> System.Collections.Generic.Dictionary.Enumerator @@ -25,44 +20,6 @@ abstract OpenTelemetry.Trace.TracerProviderBuilder.AddSource(params string![]! n ~OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.ThreadLocalRuntimeContextSlot(string name) -> void ~OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.Value.get -> object ~OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.Value.set -> void -OpenTelemetry.Metrics.IDeferredMeterProviderBuilder.Configure(System.Action! configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! -OpenTelemetry.Trace.IDeferredTracerProviderBuilder.Configure(System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! -OpenTelemetry.Trace.Link.Attributes.get -> System.Collections.Generic.IEnumerable>? -OpenTelemetry.Trace.Link.Link(in OpenTelemetry.Trace.SpanContext spanContext, OpenTelemetry.Trace.SpanAttributes? attributes) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, bool value) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, bool[]? values) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, double value) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, double[]? values) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, long value) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, long[]? values) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, string? value) -> void -OpenTelemetry.Trace.SpanAttributes.Add(string! key, string?[]? values) -> void -OpenTelemetry.Trace.SpanAttributes.SpanAttributes(System.Collections.Generic.IEnumerable>! attributes) -> void -OpenTelemetry.Trace.SpanContext.SpanContext(in System.Diagnostics.ActivityTraceId traceId, in System.Diagnostics.ActivitySpanId spanId, System.Diagnostics.ActivityTraceFlags traceFlags, bool isRemote = false, System.Collections.Generic.IEnumerable>? traceState = null) -> void -OpenTelemetry.Trace.SpanContext.TraceState.get -> System.Collections.Generic.IEnumerable>! -OpenTelemetry.Trace.Status.Description.get -> string? -OpenTelemetry.Trace.Status.WithDescription(string? description) -> OpenTelemetry.Trace.Status -OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.RecordException(string? type, string? message, string? stacktrace) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.RecordException(System.Exception? ex) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, bool value) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, bool[]? values) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, double value) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, double[]? values) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, int value) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, int[]? values) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, string? value) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, string?[]? values) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TelemetrySpan.UpdateName(string! name) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.Tracer.StartActiveSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, in OpenTelemetry.Trace.SpanContext parentContext = default(OpenTelemetry.Trace.SpanContext), OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.Tracer.StartActiveSpan(string! name, OpenTelemetry.Trace.SpanKind kind, in OpenTelemetry.Trace.TelemetrySpan? parentSpan, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.Tracer.StartRootSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, in OpenTelemetry.Trace.SpanContext parentContext = default(OpenTelemetry.Trace.SpanContext), OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind, in OpenTelemetry.Trace.TelemetrySpan? parentSpan, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! -OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version = null) -> OpenTelemetry.Trace.Tracer! ~override OpenTelemetry.Baggage.Equals(object obj) -> bool ~override OpenTelemetry.Context.Propagation.B3Propagator.Extract(OpenTelemetry.Context.Propagation.PropagationContext context, T carrier, System.Func> getter) -> OpenTelemetry.Context.Propagation.PropagationContext ~override OpenTelemetry.Context.Propagation.B3Propagator.Fields.get -> System.Collections.Generic.ISet @@ -77,10 +34,6 @@ OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version = nul ~override OpenTelemetry.Context.Propagation.TraceContextPropagator.Extract(OpenTelemetry.Context.Propagation.PropagationContext context, T carrier, System.Func> getter) -> OpenTelemetry.Context.Propagation.PropagationContext ~override OpenTelemetry.Context.Propagation.TraceContextPropagator.Fields.get -> System.Collections.Generic.ISet ~override OpenTelemetry.Context.Propagation.TraceContextPropagator.Inject(OpenTelemetry.Context.Propagation.PropagationContext context, T carrier, System.Action setter) -> void -override OpenTelemetry.Trace.Link.Equals(object? obj) -> bool -override OpenTelemetry.Trace.SpanContext.Equals(object? obj) -> bool -override OpenTelemetry.Trace.Status.Equals(object? obj) -> bool -override OpenTelemetry.Trace.Status.ToString() -> string! ~static OpenTelemetry.Baggage.Create(System.Collections.Generic.Dictionary baggageItems = null) -> OpenTelemetry.Baggage ~static OpenTelemetry.Baggage.GetBaggage(OpenTelemetry.Baggage baggage = default(OpenTelemetry.Baggage)) -> System.Collections.Generic.IReadOnlyDictionary ~static OpenTelemetry.Baggage.GetBaggage(string name, OpenTelemetry.Baggage baggage = default(OpenTelemetry.Baggage)) -> string @@ -97,15 +50,13 @@ override OpenTelemetry.Trace.Status.ToString() -> string! ~static OpenTelemetry.Context.RuntimeContext.RegisterSlot(string slotName) -> OpenTelemetry.Context.RuntimeContextSlot ~static OpenTelemetry.Context.RuntimeContext.SetValue(string slotName, object value) -> void ~static OpenTelemetry.Context.RuntimeContext.SetValue(string slotName, T value) -> void -static OpenTelemetry.Trace.ActivityExtensions.GetStatus(this System.Diagnostics.Activity! activity) -> OpenTelemetry.Trace.Status -static OpenTelemetry.Trace.ActivityExtensions.RecordException(this System.Diagnostics.Activity! activity, System.Exception? ex) -> void -static OpenTelemetry.Trace.ActivityExtensions.RecordException(this System.Diagnostics.Activity! activity, System.Exception? ex, in System.Diagnostics.TagList tags) -> void -static OpenTelemetry.Trace.ActivityExtensions.SetStatus(this System.Diagnostics.Activity! activity, OpenTelemetry.Trace.Status status) -> void -static OpenTelemetry.Trace.Tracer.CurrentSpan.get -> OpenTelemetry.Trace.TelemetrySpan! -static OpenTelemetry.Trace.Tracer.WithSpan(OpenTelemetry.Trace.TelemetrySpan? span) -> OpenTelemetry.Trace.TelemetrySpan? -static OpenTelemetry.Trace.TracerProvider.Default.get -> OpenTelemetry.Trace.TracerProvider! abstract OpenTelemetry.Context.RuntimeContextSlot.Get() -> T abstract OpenTelemetry.Context.RuntimeContextSlot.Set(T value) -> void +abstract OpenTelemetry.Metrics.MeterProviderBuilder.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Metrics.MeterProviderBuilder! +abstract OpenTelemetry.Metrics.MeterProviderBuilder.AddMeter(params string![]! names) -> OpenTelemetry.Metrics.MeterProviderBuilder! +abstract OpenTelemetry.Trace.TracerProviderBuilder.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Trace.TracerProviderBuilder! +abstract OpenTelemetry.Trace.TracerProviderBuilder.AddLegacySource(string! operationName) -> OpenTelemetry.Trace.TracerProviderBuilder! +abstract OpenTelemetry.Trace.TracerProviderBuilder.AddSource(params string![]! names) -> OpenTelemetry.Trace.TracerProviderBuilder! OpenTelemetry.ActivityContextExtensions OpenTelemetry.Baggage OpenTelemetry.Baggage.Baggage() -> void @@ -140,28 +91,43 @@ OpenTelemetry.Context.RuntimeContextSlot OpenTelemetry.Context.RuntimeContextSlot.Dispose() -> void OpenTelemetry.Context.ThreadLocalRuntimeContextSlot OpenTelemetry.Metrics.IDeferredMeterProviderBuilder +OpenTelemetry.Metrics.IDeferredMeterProviderBuilder.Configure(System.Action! configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! OpenTelemetry.Metrics.MeterProvider OpenTelemetry.Metrics.MeterProvider.MeterProvider() -> void OpenTelemetry.Metrics.MeterProviderBuilder OpenTelemetry.Metrics.MeterProviderBuilder.MeterProviderBuilder() -> void OpenTelemetry.Trace.ActivityExtensions OpenTelemetry.Trace.IDeferredTracerProviderBuilder +OpenTelemetry.Trace.IDeferredTracerProviderBuilder.Configure(System.Action! configure) -> OpenTelemetry.Trace.TracerProviderBuilder! OpenTelemetry.Trace.Link +OpenTelemetry.Trace.Link.Attributes.get -> System.Collections.Generic.IEnumerable>? OpenTelemetry.Trace.Link.Context.get -> OpenTelemetry.Trace.SpanContext OpenTelemetry.Trace.Link.Equals(OpenTelemetry.Trace.Link other) -> bool OpenTelemetry.Trace.Link.Link() -> void OpenTelemetry.Trace.Link.Link(in OpenTelemetry.Trace.SpanContext spanContext) -> void +OpenTelemetry.Trace.Link.Link(in OpenTelemetry.Trace.SpanContext spanContext, OpenTelemetry.Trace.SpanAttributes? attributes) -> void OpenTelemetry.Trace.SpanAttributes +OpenTelemetry.Trace.SpanAttributes.Add(string! key, bool value) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, bool[]? values) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, double value) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, double[]? values) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, long value) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, long[]? values) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, string? value) -> void +OpenTelemetry.Trace.SpanAttributes.Add(string! key, string?[]? values) -> void OpenTelemetry.Trace.SpanAttributes.SpanAttributes() -> void +OpenTelemetry.Trace.SpanAttributes.SpanAttributes(System.Collections.Generic.IEnumerable>! attributes) -> void OpenTelemetry.Trace.SpanContext OpenTelemetry.Trace.SpanContext.Equals(OpenTelemetry.Trace.SpanContext other) -> bool OpenTelemetry.Trace.SpanContext.IsRemote.get -> bool OpenTelemetry.Trace.SpanContext.IsValid.get -> bool OpenTelemetry.Trace.SpanContext.SpanContext() -> void OpenTelemetry.Trace.SpanContext.SpanContext(in System.Diagnostics.ActivityContext activityContext) -> void +OpenTelemetry.Trace.SpanContext.SpanContext(in System.Diagnostics.ActivityTraceId traceId, in System.Diagnostics.ActivitySpanId spanId, System.Diagnostics.ActivityTraceFlags traceFlags, bool isRemote = false, System.Collections.Generic.IEnumerable>? traceState = null) -> void OpenTelemetry.Trace.SpanContext.SpanId.get -> System.Diagnostics.ActivitySpanId OpenTelemetry.Trace.SpanContext.TraceFlags.get -> System.Diagnostics.ActivityTraceFlags OpenTelemetry.Trace.SpanContext.TraceId.get -> System.Diagnostics.ActivityTraceId +OpenTelemetry.Trace.SpanContext.TraceState.get -> System.Collections.Generic.IEnumerable>! OpenTelemetry.Trace.SpanKind OpenTelemetry.Trace.SpanKind.Client = 3 -> OpenTelemetry.Trace.SpanKind OpenTelemetry.Trace.SpanKind.Consumer = 5 -> OpenTelemetry.Trace.SpanKind @@ -169,23 +135,46 @@ OpenTelemetry.Trace.SpanKind.Internal = 1 -> OpenTelemetry.Trace.SpanKind OpenTelemetry.Trace.SpanKind.Producer = 4 -> OpenTelemetry.Trace.SpanKind OpenTelemetry.Trace.SpanKind.Server = 2 -> OpenTelemetry.Trace.SpanKind OpenTelemetry.Trace.Status +OpenTelemetry.Trace.Status.Description.get -> string? OpenTelemetry.Trace.Status.Equals(OpenTelemetry.Trace.Status other) -> bool OpenTelemetry.Trace.Status.Status() -> void OpenTelemetry.Trace.Status.StatusCode.get -> OpenTelemetry.Trace.StatusCode +OpenTelemetry.Trace.Status.WithDescription(string? description) -> OpenTelemetry.Trace.Status OpenTelemetry.Trace.StatusCode OpenTelemetry.Trace.StatusCode.Error = 2 -> OpenTelemetry.Trace.StatusCode OpenTelemetry.Trace.StatusCode.Ok = 1 -> OpenTelemetry.Trace.StatusCode OpenTelemetry.Trace.StatusCode.Unset = 0 -> OpenTelemetry.Trace.StatusCode OpenTelemetry.Trace.TelemetrySpan +OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.AddEvent(string! name, System.DateTimeOffset timestamp, OpenTelemetry.Trace.SpanAttributes? attributes) -> OpenTelemetry.Trace.TelemetrySpan! OpenTelemetry.Trace.TelemetrySpan.Context.get -> OpenTelemetry.Trace.SpanContext OpenTelemetry.Trace.TelemetrySpan.Dispose() -> void OpenTelemetry.Trace.TelemetrySpan.End() -> void OpenTelemetry.Trace.TelemetrySpan.End(System.DateTimeOffset endTimestamp) -> void OpenTelemetry.Trace.TelemetrySpan.IsRecording.get -> bool OpenTelemetry.Trace.TelemetrySpan.ParentSpanId.get -> System.Diagnostics.ActivitySpanId +OpenTelemetry.Trace.TelemetrySpan.RecordException(string? type, string? message, string? stacktrace) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.RecordException(System.Exception? ex) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, bool value) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, bool[]? values) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, double value) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, double[]? values) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, int value) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, int[]? values) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, string? value) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.TelemetrySpan.SetAttribute(string! key, string?[]? values) -> OpenTelemetry.Trace.TelemetrySpan! OpenTelemetry.Trace.TelemetrySpan.SetStatus(OpenTelemetry.Trace.Status value) -> void +OpenTelemetry.Trace.TelemetrySpan.UpdateName(string! name) -> OpenTelemetry.Trace.TelemetrySpan! OpenTelemetry.Trace.Tracer +OpenTelemetry.Trace.Tracer.StartActiveSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, in OpenTelemetry.Trace.SpanContext parentContext = default(OpenTelemetry.Trace.SpanContext), OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.Tracer.StartActiveSpan(string! name, OpenTelemetry.Trace.SpanKind kind, in OpenTelemetry.Trace.TelemetrySpan? parentSpan, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.Tracer.StartRootSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind = OpenTelemetry.Trace.SpanKind.Internal, in OpenTelemetry.Trace.SpanContext parentContext = default(OpenTelemetry.Trace.SpanContext), OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! +OpenTelemetry.Trace.Tracer.StartSpan(string! name, OpenTelemetry.Trace.SpanKind kind, in OpenTelemetry.Trace.TelemetrySpan? parentSpan, OpenTelemetry.Trace.SpanAttributes? initialAttributes = null, System.Collections.Generic.IEnumerable? links = null, System.DateTimeOffset startTime = default(System.DateTimeOffset)) -> OpenTelemetry.Trace.TelemetrySpan! OpenTelemetry.Trace.TracerProvider +OpenTelemetry.Trace.TracerProvider.GetTracer(string! name, string? version = null) -> OpenTelemetry.Trace.Tracer! OpenTelemetry.Trace.TracerProvider.TracerProvider() -> void OpenTelemetry.Trace.TracerProviderBuilder OpenTelemetry.Trace.TracerProviderBuilder.TracerProviderBuilder() -> void @@ -196,9 +185,14 @@ override OpenTelemetry.Context.Propagation.PropagationContext.GetHashCode() -> i override OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.Dispose(bool disposing) -> void override OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.Get() -> T override OpenTelemetry.Context.ThreadLocalRuntimeContextSlot.Set(T value) -> void +override OpenTelemetry.Trace.Link.Equals(object? obj) -> bool override OpenTelemetry.Trace.Link.GetHashCode() -> int +override OpenTelemetry.Trace.SpanContext.Equals(object? obj) -> bool override OpenTelemetry.Trace.SpanContext.GetHashCode() -> int +override OpenTelemetry.Trace.Status.Equals(object? obj) -> bool override OpenTelemetry.Trace.Status.GetHashCode() -> int +override OpenTelemetry.Trace.Status.ToString() -> string! +override OpenTelemetry.Trace.TracerProvider.Dispose(bool disposing) -> void static OpenTelemetry.ActivityContextExtensions.IsValid(this System.Diagnostics.ActivityContext ctx) -> bool static OpenTelemetry.Baggage.ClearBaggage(OpenTelemetry.Baggage baggage = default(OpenTelemetry.Baggage)) -> OpenTelemetry.Baggage static OpenTelemetry.Baggage.Current.get -> OpenTelemetry.Baggage @@ -207,6 +201,10 @@ static OpenTelemetry.Baggage.operator !=(OpenTelemetry.Baggage left, OpenTelemet static OpenTelemetry.Baggage.operator ==(OpenTelemetry.Baggage left, OpenTelemetry.Baggage right) -> bool static OpenTelemetry.Context.Propagation.PropagationContext.operator !=(OpenTelemetry.Context.Propagation.PropagationContext left, OpenTelemetry.Context.Propagation.PropagationContext right) -> bool static OpenTelemetry.Context.Propagation.PropagationContext.operator ==(OpenTelemetry.Context.Propagation.PropagationContext left, OpenTelemetry.Context.Propagation.PropagationContext right) -> bool +static OpenTelemetry.Trace.ActivityExtensions.GetStatus(this System.Diagnostics.Activity! activity) -> OpenTelemetry.Trace.Status +static OpenTelemetry.Trace.ActivityExtensions.RecordException(this System.Diagnostics.Activity! activity, System.Exception? ex) -> void +static OpenTelemetry.Trace.ActivityExtensions.RecordException(this System.Diagnostics.Activity! activity, System.Exception? ex, in System.Diagnostics.TagList tags) -> void +static OpenTelemetry.Trace.ActivityExtensions.SetStatus(this System.Diagnostics.Activity! activity, OpenTelemetry.Trace.Status status) -> void static OpenTelemetry.Trace.Link.operator !=(OpenTelemetry.Trace.Link link1, OpenTelemetry.Trace.Link link2) -> bool static OpenTelemetry.Trace.Link.operator ==(OpenTelemetry.Trace.Link link1, OpenTelemetry.Trace.Link link2) -> bool static OpenTelemetry.Trace.SpanContext.implicit operator System.Diagnostics.ActivityContext(OpenTelemetry.Trace.SpanContext spanContext) -> System.Diagnostics.ActivityContext @@ -214,6 +212,9 @@ static OpenTelemetry.Trace.SpanContext.operator !=(OpenTelemetry.Trace.SpanConte static OpenTelemetry.Trace.SpanContext.operator ==(OpenTelemetry.Trace.SpanContext spanContext1, OpenTelemetry.Trace.SpanContext spanContext2) -> bool static OpenTelemetry.Trace.Status.operator !=(OpenTelemetry.Trace.Status status1, OpenTelemetry.Trace.Status status2) -> bool static OpenTelemetry.Trace.Status.operator ==(OpenTelemetry.Trace.Status status1, OpenTelemetry.Trace.Status status2) -> bool +static OpenTelemetry.Trace.Tracer.CurrentSpan.get -> OpenTelemetry.Trace.TelemetrySpan! +static OpenTelemetry.Trace.Tracer.WithSpan(OpenTelemetry.Trace.TelemetrySpan? span) -> OpenTelemetry.Trace.TelemetrySpan? +static OpenTelemetry.Trace.TracerProvider.Default.get -> OpenTelemetry.Trace.TracerProvider! static readonly OpenTelemetry.Trace.Status.Error -> OpenTelemetry.Trace.Status static readonly OpenTelemetry.Trace.Status.Ok -> OpenTelemetry.Trace.Status static readonly OpenTelemetry.Trace.Status.Unset -> OpenTelemetry.Trace.Status diff --git a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Unshipped.txt index b3db39f4c4..e69de29bb2 100644 --- a/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1 +0,0 @@ -override OpenTelemetry.Trace.TracerProvider.Dispose(bool disposing) -> void diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 9ec4e76c4a..033331f895 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Api/Logs/IDeferredLoggerProviderBuilder.cs b/src/OpenTelemetry.Api/Logs/IDeferredLoggerProviderBuilder.cs index b7f5f3b081..70528fe6cb 100644 --- a/src/OpenTelemetry.Api/Logs/IDeferredLoggerProviderBuilder.cs +++ b/src/OpenTelemetry.Api/Logs/IDeferredLoggerProviderBuilder.cs @@ -3,6 +3,11 @@ #nullable enable +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Logs; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -12,6 +17,9 @@ namespace OpenTelemetry.Logs; /// dependency injection. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -21,7 +29,7 @@ namespace OpenTelemetry.Logs; /// internal #endif - interface IDeferredLoggerProviderBuilder +interface IDeferredLoggerProviderBuilder { /// /// Register a callback action to configure the /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Api/Logs/LogRecordData.cs b/src/OpenTelemetry.Api/Logs/LogRecordData.cs index ab5af38526..cb3c49292a 100644 --- a/src/OpenTelemetry.Api/Logs/LogRecordData.cs +++ b/src/OpenTelemetry.Api/Logs/LogRecordData.cs @@ -4,6 +4,10 @@ #nullable enable using System.Diagnostics; +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif namespace OpenTelemetry.Logs; @@ -12,6 +16,9 @@ namespace OpenTelemetry.Logs; /// Stores details about a log message. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs b/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs index 760f688299..9f48e71e85 100644 --- a/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs +++ b/src/OpenTelemetry.Api/Logs/LogRecordSeverity.cs @@ -3,6 +3,11 @@ #nullable enable +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Logs; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -10,6 +15,9 @@ namespace OpenTelemetry.Logs; /// Describes the severity level of a log record. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs b/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs index c7f180294b..f171edbc9c 100644 --- a/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs +++ b/src/OpenTelemetry.Api/Logs/LogRecordSeverityExtensions.cs @@ -3,6 +3,11 @@ #nullable enable +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Logs; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -10,6 +15,9 @@ namespace OpenTelemetry.Logs; /// Contains extension methods for the enum. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Api/Logs/Logger.cs b/src/OpenTelemetry.Api/Logs/Logger.cs index 6db679016f..71baf7d611 100644 --- a/src/OpenTelemetry.Api/Logs/Logger.cs +++ b/src/OpenTelemetry.Api/Logs/Logger.cs @@ -3,6 +3,11 @@ #nullable enable +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Logs; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -10,6 +15,9 @@ namespace OpenTelemetry.Logs; /// Logger is the class responsible for creating log records. /// /// WARNING: This is an experimental API which might change or be removed in the future. Use at your own risk. +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -17,7 +25,7 @@ namespace OpenTelemetry.Logs; /// internal #endif - abstract class Logger +abstract class Logger { /// /// Initializes a new instance of the class. diff --git a/src/OpenTelemetry.Api/Logs/LoggerProvider.cs b/src/OpenTelemetry.Api/Logs/LoggerProvider.cs index fd8099715c..c7993dc664 100644 --- a/src/OpenTelemetry.Api/Logs/LoggerProvider.cs +++ b/src/OpenTelemetry.Api/Logs/LoggerProvider.cs @@ -6,6 +6,9 @@ #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif +#if NET8_0_OR_GREATER +using OpenTelemetry.Internal; +#endif namespace OpenTelemetry.Logs; @@ -14,6 +17,9 @@ namespace OpenTelemetry.Logs; /// LoggerProvider is the entry point of the OpenTelemetry API. It provides access to . /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -36,6 +42,9 @@ protected LoggerProvider() /// Gets a logger. /// /// instance. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public Logger GetLogger() => this.GetLogger(name: null, version: null); @@ -44,6 +53,9 @@ public Logger GetLogger() /// /// Optional name identifying the instrumentation library. /// instance. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public Logger GetLogger(string? name) => this.GetLogger(name, version: null); @@ -53,6 +65,9 @@ public Logger GetLogger(string? name) /// Optional name identifying the instrumentation library. /// Optional version of the instrumentation library. /// instance. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public Logger GetLogger(string? name, string? version) { if (!this.TryCreateLogger(name, out var logger)) @@ -71,6 +86,9 @@ public Logger GetLogger(string? name, string? version) /// Optional name identifying the instrumentation library. /// . /// if the logger was created. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif protected virtual bool TryCreateLogger( string? name, #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER diff --git a/src/OpenTelemetry.Api/Logs/LoggerProviderBuilder.cs b/src/OpenTelemetry.Api/Logs/LoggerProviderBuilder.cs index 617a51ead0..3fa9c6fc74 100644 --- a/src/OpenTelemetry.Api/Logs/LoggerProviderBuilder.cs +++ b/src/OpenTelemetry.Api/Logs/LoggerProviderBuilder.cs @@ -3,6 +3,11 @@ #nullable enable +#if NET8_0_OR_GREATER && EXPOSE_EXPERIMENTAL_FEATURES +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Logs; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -10,6 +15,9 @@ namespace OpenTelemetry.Logs; /// LoggerProviderBuilder base class. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj index 27aa7389a8..0a781a9417 100644 --- a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj +++ b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj @@ -15,6 +15,7 @@ + diff --git a/src/OpenTelemetry.Api/Trace/ActivityExtensions.cs b/src/OpenTelemetry.Api/Trace/ActivityExtensions.cs index 036d8c11a7..d8ac769b9c 100644 --- a/src/OpenTelemetry.Api/Trace/ActivityExtensions.cs +++ b/src/OpenTelemetry.Api/Trace/ActivityExtensions.cs @@ -57,20 +57,26 @@ public static Status GetStatus(this Activity activity) } /// - /// Adds an activity event containing information from the specified exception. + /// Adds an containing information from the specified exception. /// /// Activity instance. /// Exception to be recorded. + /// The exception is recorded as per specification. + /// "exception.stacktrace" is represented using the value of Exception.ToString. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RecordException(this Activity activity, Exception? ex) => RecordException(activity, ex, default); /// - /// Adds an activity event containing information from the specified exception and additional tags. + /// Adds an containing information from the specified exception and additional tags. /// /// Activity instance. /// Exception to be recorded. /// Additional tags to record on the event. + /// The exception is recorded as per specification. + /// "exception.stacktrace" is represented using the value of Exception.ToString. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RecordException(this Activity activity, Exception? ex, in TagList tags) { diff --git a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md index 953a736999..043a395cc5 100644 --- a/src/OpenTelemetry.Exporter.Console/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Console/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs b/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs index afd5a15a3a..6b024498cb 100644 --- a/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs +++ b/src/OpenTelemetry.Exporter.Console/ConsoleExporterLoggingExtensions.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenTelemetry.Exporter; @@ -42,6 +45,9 @@ public static OpenTelemetryLoggerOptions AddConsoleExporter(this OpenTelemetryLo /// WARNING: This is an experimental API which might change or be removed in the future. Use at your own risk. /// . /// The supplied instance of to chain the calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -63,6 +69,9 @@ static LoggerProviderBuilder AddConsoleExporter( /// . /// Callback action for configuring . /// The supplied instance of to chain the calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -87,6 +96,9 @@ static LoggerProviderBuilder AddConsoleExporter( /// Name which is used when retrieving options. /// Callback action for configuring . /// The supplied instance of to chain the calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Exporter.Console/OpenTelemetry.Exporter.Console.csproj b/src/OpenTelemetry.Exporter.Console/OpenTelemetry.Exporter.Console.csproj index 2cea6f1094..46fec7d5e9 100644 --- a/src/OpenTelemetry.Exporter.Console/OpenTelemetry.Exporter.Console.csproj +++ b/src/OpenTelemetry.Exporter.Console/OpenTelemetry.Exporter.Console.csproj @@ -32,6 +32,7 @@ + diff --git a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md index db39656ec8..42ad0c3b52 100644 --- a/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs index 855c96f957..982595f882 100644 --- a/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs +++ b/src/OpenTelemetry.Exporter.InMemory/InMemoryExporterLoggingExtensions.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using OpenTelemetry.Exporter; using OpenTelemetry.Internal; @@ -36,6 +39,9 @@ public static OpenTelemetryLoggerOptions AddInMemoryExporter( /// . /// Collection which will be populated with the exported . /// The supplied instance of to chain the calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj b/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj index ca347c71ba..7f7488e8c5 100644 --- a/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj +++ b/src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj @@ -21,9 +21,10 @@ - + + diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Shipped.txt index cd9c686fd1..4b7af04643 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Shipped.txt @@ -1,15 +1,19 @@ #nullable enable -OpenTelemetry.Exporter.OtlpExporterOptions ~OpenTelemetry.Exporter.OtlpExporterOptions.BatchExportProcessorOptions.get -> OpenTelemetry.BatchExportProcessorOptions ~OpenTelemetry.Exporter.OtlpExporterOptions.BatchExportProcessorOptions.set -> void ~OpenTelemetry.Exporter.OtlpExporterOptions.Endpoint.get -> System.Uri ~OpenTelemetry.Exporter.OtlpExporterOptions.Endpoint.set -> void -OpenTelemetry.Exporter.OtlpExporterOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType -OpenTelemetry.Exporter.OtlpExporterOptions.ExportProcessorType.set -> void ~OpenTelemetry.Exporter.OtlpExporterOptions.Headers.get -> string ~OpenTelemetry.Exporter.OtlpExporterOptions.Headers.set -> void ~OpenTelemetry.Exporter.OtlpExporterOptions.HttpClientFactory.get -> System.Func ~OpenTelemetry.Exporter.OtlpExporterOptions.HttpClientFactory.set -> void +~OpenTelemetry.Exporter.OtlpMetricExporter.OtlpMetricExporter(OpenTelemetry.Exporter.OtlpExporterOptions options) -> void +~OpenTelemetry.Exporter.OtlpTraceExporter.OtlpTraceExporter(OpenTelemetry.Exporter.OtlpExporterOptions options) -> void +~override OpenTelemetry.Exporter.OtlpMetricExporter.Export(in OpenTelemetry.Batch metrics) -> OpenTelemetry.ExportResult +~override OpenTelemetry.Exporter.OtlpTraceExporter.Export(in OpenTelemetry.Batch activityBatch) -> OpenTelemetry.ExportResult +OpenTelemetry.Exporter.OtlpExporterOptions +OpenTelemetry.Exporter.OtlpExporterOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType +OpenTelemetry.Exporter.OtlpExporterOptions.ExportProcessorType.set -> void OpenTelemetry.Exporter.OtlpExporterOptions.OtlpExporterOptions() -> void OpenTelemetry.Exporter.OtlpExporterOptions.Protocol.get -> OpenTelemetry.Exporter.OtlpExportProtocol OpenTelemetry.Exporter.OtlpExporterOptions.Protocol.set -> void @@ -18,18 +22,19 @@ OpenTelemetry.Exporter.OtlpExporterOptions.TimeoutMilliseconds.set -> void OpenTelemetry.Exporter.OtlpExportProtocol OpenTelemetry.Exporter.OtlpExportProtocol.Grpc = 0 -> OpenTelemetry.Exporter.OtlpExportProtocol OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf = 1 -> OpenTelemetry.Exporter.OtlpExportProtocol +OpenTelemetry.Exporter.OtlpLogExporter +OpenTelemetry.Exporter.OtlpLogExporter.OtlpLogExporter(OpenTelemetry.Exporter.OtlpExporterOptions! options) -> void OpenTelemetry.Exporter.OtlpMetricExporter -~OpenTelemetry.Exporter.OtlpMetricExporter.OtlpMetricExporter(OpenTelemetry.Exporter.OtlpExporterOptions options) -> void OpenTelemetry.Exporter.OtlpTraceExporter -~OpenTelemetry.Exporter.OtlpTraceExporter.OtlpTraceExporter(OpenTelemetry.Exporter.OtlpExporterOptions options) -> void OpenTelemetry.Logs.OtlpLogExporterHelperExtensions OpenTelemetry.Metrics.OtlpMetricExporterExtensions OpenTelemetry.Trace.OtlpTraceExporterHelperExtensions -~override OpenTelemetry.Exporter.OtlpMetricExporter.Export(in OpenTelemetry.Batch metrics) -> OpenTelemetry.ExportResult +override OpenTelemetry.Exporter.OtlpLogExporter.Export(in OpenTelemetry.Batch logRecordBatch) -> OpenTelemetry.ExportResult override OpenTelemetry.Exporter.OtlpMetricExporter.OnShutdown(int timeoutMilliseconds) -> bool -~override OpenTelemetry.Exporter.OtlpTraceExporter.Export(in OpenTelemetry.Batch activityBatch) -> OpenTelemetry.ExportResult override OpenTelemetry.Exporter.OtlpTraceExporter.OnShutdown(int timeoutMilliseconds) -> bool static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, string? name, System.Action? configureExporterAndProcessor) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, string? name, System.Action? configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, System.Action! configureExporterAndProcessor) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! static OpenTelemetry.Metrics.OtlpMetricExporterExtensions.AddOtlpExporter(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder! diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt index f90e2e4dfb..e69de29bb2 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1,6 +0,0 @@ -#nullable enable -OpenTelemetry.Exporter.OtlpLogExporter -OpenTelemetry.Exporter.OtlpLogExporter.OtlpLogExporter(OpenTelemetry.Exporter.OtlpExporterOptions! options) -> void -override OpenTelemetry.Exporter.OtlpLogExporter.Export(in OpenTelemetry.Batch logRecordBatch) -> OpenTelemetry.ExportResult -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, string? name, System.Action? configureExporterAndProcessor) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! -static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, string? name, System.Action? configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 5b4498446c..284ce9c8e6 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 809994f353..ba6c9ba702 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -3,6 +3,9 @@ ## Unreleased * Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107)) +* For requests with OpenMetrics format, scope info is automatically added + ([#5086](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5086) + [#5182](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5182)) ## 1.7.0-rc.1 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 16185be88d..aa2981a5e1 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -3,6 +3,9 @@ ## Unreleased * Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107)) +* For requests with OpenMetrics format, scope info is automatically added + ([#5086](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5086) + [#5182](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5182)) ## 1.7.0-rc.1 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index e60047739e..b8b4f4888a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -14,6 +14,7 @@ internal sealed class PrometheusCollectionManager private readonly int scrapeResponseCacheDurationMilliseconds; private readonly Func, ExportResult> onCollectRef; private readonly Dictionary metricsCache; + private readonly HashSet scopes; private int metricsCacheCount; private byte[] buffer = new byte[85000]; // encourage the object to live in LOH (large object heap) private int globalLockState; @@ -29,6 +30,7 @@ public PrometheusCollectionManager(PrometheusExporter exporter) this.scrapeResponseCacheDurationMilliseconds = this.exporter.ScrapeResponseCacheDurationMilliseconds; this.onCollectRef = this.OnCollect; this.metricsCache = new Dictionary(); + this.scopes = new HashSet(); } #if NET6_0_OR_GREATER @@ -170,6 +172,40 @@ private ExportResult OnCollect(Batch metrics) try { + if (this.exporter.OpenMetricsRequested) + { + this.scopes.Clear(); + + foreach (var metric in metrics) + { + if (PrometheusSerializer.CanWriteMetric(metric)) + { + if (this.scopes.Add(metric.MeterName)) + { + try + { + cursor = PrometheusSerializer.WriteScopeInfo(this.buffer, cursor, metric.MeterName); + + break; + } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + // there are two cases we might run into the following condition: + // 1. we have many metrics to be exported - in this case we probably want + // to put some upper limit and allow the user to configure it. + // 2. we got an IndexOutOfRangeException which was triggered by some other + // code instead of the buffer[cursor++] - in this case we should give up + // at certain point rather than allocating like crazy. + throw; + } + } + } + } + } + } + foreach (var metric in metrics) { if (!PrometheusSerializer.CanWriteMetric(metric)) @@ -194,12 +230,6 @@ private ExportResult OnCollect(Batch metrics) { if (!this.IncreaseBufferSize()) { - // there are two cases we might run into the following condition: - // 1. we have many metrics to be exported - in this case we probably want - // to put some upper limit and allow the user to configure it. - // 2. we got an IndexOutOfRangeException which was triggered by some other - // code instead of the buffer[cursor++] - in this case we should give up - // at certain point rather than allocating like crazy. throw; } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index 7c3fc57ba0..69365d4e0f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using OpenTelemetry.Internal; +using OpenTelemetry.Metrics; namespace OpenTelemetry.Exporter.Prometheus; @@ -313,6 +314,31 @@ public static int WriteUnitMetadata(byte[] buffer, int cursor, PrometheusMetric return cursor; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int WriteScopeInfo(byte[] buffer, int cursor, string scopeName) + { + if (string.IsNullOrEmpty(scopeName)) + { + return cursor; + } + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "# TYPE otel_scope_info info"); + buffer[cursor++] = ASCII_LINEFEED; + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "# HELP otel_scope_info Scope metadata"); + buffer[cursor++] = ASCII_LINEFEED; + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "otel_scope_info"); + buffer[cursor++] = unchecked((byte)'{'); + cursor = WriteLabel(buffer, cursor, "otel_scope_name", scopeName); + buffer[cursor++] = unchecked((byte)'}'); + buffer[cursor++] = unchecked((byte)' '); + buffer[cursor++] = unchecked((byte)'1'); + buffer[cursor++] = ASCII_LINEFEED; + + return cursor; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteTimestamp(byte[] buffer, int cursor, long value, bool useOpenMetrics) { @@ -339,6 +365,37 @@ public static int WriteTimestamp(byte[] buffer, int cursor, long value, bool use return WriteLong(buffer, cursor, value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTagCollection tags, bool writeEnclosingBraces = true) + { + if (writeEnclosingBraces) + { + buffer[cursor++] = unchecked((byte)'{'); + } + + cursor = WriteLabel(buffer, cursor, "otel_scope_name", metric.MeterName); + buffer[cursor++] = unchecked((byte)','); + + if (!string.IsNullOrEmpty(metric.MeterVersion)) + { + cursor = WriteLabel(buffer, cursor, "otel_scope_version", metric.MeterVersion); + buffer[cursor++] = unchecked((byte)','); + } + + foreach (var tag in tags) + { + cursor = WriteLabel(buffer, cursor, tag.Key, tag.Value); + buffer[cursor++] = unchecked((byte)','); + } + + if (writeEnclosingBraces) + { + buffer[cursor - 1] = unchecked((byte)'}'); // Note: We write the '}' over the last written comma, which is extra. + } + + return cursor; + } + private static string MapPrometheusType(PrometheusType type) { return type switch diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index fa9885a44b..1523ef7c16 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs @@ -32,24 +32,11 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe { foreach (ref readonly var metricPoint in metric.GetMetricPoints()) { - var tags = metricPoint.Tags; var timestamp = metricPoint.EndTime.ToUnixTimeMilliseconds(); // Counter and Gauge cursor = WriteMetricName(buffer, cursor, prometheusMetric); - - if (tags.Count > 0) - { - buffer[cursor++] = unchecked((byte)'{'); - - foreach (var tag in tags) - { - cursor = WriteLabel(buffer, cursor, tag.Key, tag.Value); - buffer[cursor++] = unchecked((byte)','); - } - - buffer[cursor - 1] = unchecked((byte)'}'); // Note: We write the '}' over the last written comma, which is extra. - } + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); @@ -100,12 +87,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_bucket{"); - - foreach (var tag in tags) - { - cursor = WriteLabel(buffer, cursor, tag.Key, tag.Value); - buffer[cursor++] = unchecked((byte)','); - } + cursor = WriteTags(buffer, cursor, metric, tags, writeEnclosingBraces: false); cursor = WriteAsciiStringNoEscape(buffer, cursor, "le=\""); @@ -131,19 +113,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram sum cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_sum"); - - if (tags.Count > 0) - { - buffer[cursor++] = unchecked((byte)'{'); - - foreach (var tag in tags) - { - cursor = WriteLabel(buffer, cursor, tag.Key, tag.Value); - buffer[cursor++] = unchecked((byte)','); - } - - buffer[cursor - 1] = unchecked((byte)'}'); // Note: We write the '}' over the last written comma, which is extra. - } + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); @@ -157,19 +127,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram count cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_count"); - - if (tags.Count > 0) - { - buffer[cursor++] = unchecked((byte)'{'); - - foreach (var tag in tags) - { - cursor = WriteLabel(buffer, cursor, tag.Key, tag.Value); - buffer[cursor++] = unchecked((byte)','); - } - - buffer[cursor - 1] = unchecked((byte)'}'); // Note: We write the '}' over the last written comma, which is extra. - } + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); diff --git a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md index 0e26e87ca6..babc948310 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Extensions.Hosting/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.Hosting/.publicApi/Stable/PublicAPI.Unshipped.txt index 8b13789179..e69de29bb2 100644 --- a/src/OpenTelemetry.Extensions.Hosting/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Extensions.Hosting/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1 +0,0 @@ - diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index 9170bc3610..ef7152a120 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj index cc6b1b078a..5e798a19f6 100644 --- a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj +++ b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetry.Extensions.Hosting.csproj @@ -18,9 +18,10 @@ - + + diff --git a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs index c4820457ae..3037f1f162 100644 --- a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs +++ b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Logging; @@ -148,6 +151,9 @@ public OpenTelemetryBuilder WithTracing(Action configure) /// /// The supplied for chaining /// calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -180,6 +186,9 @@ OpenTelemetryBuilder WithLogging() /// configuration callback. /// The supplied for chaining /// calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -210,6 +219,9 @@ OpenTelemetryBuilder WithLogging(Action configure) /// cref="OpenTelemetryLoggerOptions"/> configuration callback. /// The supplied for chaining /// calls. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md index 0e62ed845b..bf85dec546 100644 --- a/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Propagators/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt index e69de29bb2..fc47928891 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt @@ -0,0 +1,18 @@ +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.AspNetCoreTraceInstrumentationOptions() -> void +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.get -> System.Action +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.set -> void +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.get -> System.Action +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.set -> void +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.get -> System.Action +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.set -> void +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.get -> System.Func +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.set -> void +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.get -> bool +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.set -> void +OpenTelemetry.Metrics.AspNetCoreInstrumentationMeterProviderBuilderExtensions +OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions +static OpenTelemetry.Metrics.AspNetCoreInstrumentationMeterProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder +static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder +static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 859e71b936..e69de29bb2 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt @@ -1,18 +0,0 @@ -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.AspNetCoreTraceInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.get -> System.Func -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.get -> bool -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.set -> void -OpenTelemetry.Metrics.MeterProviderBuilderExtensions -OpenTelemetry.Trace.TracerProviderBuilderExtensions -static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index d72ec2c4d3..e69de29bb2 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,2 +0,0 @@ -*REMOVED*OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentationOptions.EnableGrpcAspNetCoreSupport.get -> bool -*REMOVED*OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentationOptions.EnableGrpcAspNetCoreSupport.set -> void diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs similarity index 95% rename from src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs rename to src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs index 925575ff40..3da8c1de59 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs @@ -12,7 +12,7 @@ namespace OpenTelemetry.Metrics; /// /// Extension methods to simplify registering of ASP.NET Core request instrumentation. /// -public static class MeterProviderBuilderExtensions +public static class AspNetCoreInstrumentationMeterProviderBuilderExtensions { /// /// Enables the incoming requests automatic data collection for ASP.NET Core. diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs similarity index 96% rename from src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs rename to src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs index 38919cb115..d37d77ae3f 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs @@ -15,7 +15,7 @@ namespace OpenTelemetry.Trace; /// /// Extension methods to simplify registering of ASP.NET Core request instrumentation. /// -public static class TracerProviderBuilderExtensions +public static class AspNetCoreInstrumentationTracerProviderBuilderExtensions { /// /// Enables the incoming requests automatic data collection for ASP.NET Core. @@ -62,6 +62,8 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation( { services.Configure(name, configureAspNetCoreTraceInstrumentationOptions); } + + services.RegisterOptionsFactory(configuration => new AspNetCoreTraceInstrumentationOptions(configuration)); }); if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs index 9c6f210a71..f66a5e0434 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Internal; namespace OpenTelemetry.Instrumentation.AspNetCore; @@ -11,6 +13,24 @@ namespace OpenTelemetry.Instrumentation.AspNetCore; /// public class AspNetCoreTraceInstrumentationOptions { + /// + /// Initializes a new instance of the class. + /// + public AspNetCoreTraceInstrumentationOptions() + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) + { + } + + internal AspNetCoreTraceInstrumentationOptions(IConfiguration configuration) + { + Debug.Assert(configuration != null, "configuration was null"); + + if (configuration.TryGetBoolValue("OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION", out var enableGrpcInstrumentation)) + { + this.EnableGrpcAspNetCoreSupport = enableGrpcInstrumentation; + } + } + /// /// Gets or sets a filter function that determines whether or not to /// collect telemetry on a per request basis. @@ -64,17 +84,11 @@ public class AspNetCoreTraceInstrumentationOptions /// public bool RecordException { get; set; } - /* - * Removing for stable release of http instrumentation. - * grpc semantic conventions are not yet stable so this option will not be part of stable package. - #if NET6_0_OR_GREATER - /// - /// Gets or sets a value indicating whether RPC attributes are added to an Activity when using Grpc.AspNetCore. Default is true. - /// - /// - /// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md. - /// - public bool EnableGrpcAspNetCoreSupport { get; set; } = true; - #endif - */ + /// + /// Gets or sets a value indicating whether RPC attributes are added to an Activity when using Grpc.AspNetCore. + /// + /// + /// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md. + /// + internal bool EnableGrpcAspNetCoreSupport { get; set; } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index d6dc29b65c..df628f13cc 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,6 +2,28 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-13 + +## 1.6.0 - First stable release of this library + +Released 2023-Dec-13 + +* Re-introduced support for gRPC instrumentation as an opt-in experimental + feature. From now onwards, gRPC can be enabled by setting + `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` flag to + `True`. `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` can + be set as an environment variable or via IConfiguration. The change is + introduced in order to support stable release of `http` instrumentation. + Semantic conventions for RPC is still + [experimental](https://github.com/open-telemetry/semantic-conventions/tree/main/docs/rpc) + and hence the package will only support it as an opt-in experimental feature. + Note that the support was removed in `1.6.0-rc.1` version of the package and + versions released before `1.6.0-rc.1` had gRPC instrumentation enabled by + default. + ([#5130](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5130)) + ## 1.6.0-rc.1 Released 2023-Dec-01 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 67a79c50eb..a9aefacc00 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -6,17 +6,13 @@ using System.Diagnostics.CodeAnalysis; #endif using System.Reflection; -#if !NETSTANDARD2_0 using System.Runtime.CompilerServices; -#endif using Microsoft.AspNetCore.Http; #if !NETSTANDARD using Microsoft.AspNetCore.Routing; #endif using OpenTelemetry.Context.Propagation; -#if !NETSTANDARD2_0 using OpenTelemetry.Instrumentation.GrpcNetClient; -#endif using OpenTelemetry.Internal; using OpenTelemetry.Trace; @@ -43,7 +39,17 @@ internal class HttpInListener : ListenerHandler private const string DiagnosticSourceName = "Microsoft.AspNetCore"; private const string UnknownHostName = "UNKNOWN-HOST"; - private static readonly Func> HttpRequestHeaderValuesGetter = (request, name) => request.Headers[name]; + private static readonly Func> HttpRequestHeaderValuesGetter = (request, name) => + { + if (request.Headers.TryGetValue(name, out var value)) + { + // This causes allocation as the `StringValues` struct has to be casted to an `IEnumerable` object. + return value; + } + + return Enumerable.Empty(); + }; + private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); #if !NET6_0_OR_GREATER @@ -170,7 +176,7 @@ public void OnStartActivity(Activity activity, object payload) #endif var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/"; - activity.DisplayName = this.GetDisplayName(request.Method); + activity.DisplayName = GetDisplayName(request.Method); // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md @@ -233,20 +239,18 @@ public void OnStopActivity(Activity activity, object payload) var routePattern = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; if (!string.IsNullOrEmpty(routePattern)) { - activity.DisplayName = this.GetDisplayName(context.Request.Method, routePattern); + activity.DisplayName = GetDisplayName(context.Request.Method, routePattern); activity.SetTag(SemanticConventions.AttributeHttpRoute, routePattern); } #endif activity.SetTag(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(response.StatusCode)); - /* - #if !NETSTANDARD2_0 - - if (this.options.EnableGrpcAspNetCoreSupport && TryGetGrpcMethod(activity, out var grpcMethod)) - { - this.AddGrpcAttributes(activity, grpcMethod, context); - } - */ + + if (this.options.EnableGrpcAspNetCoreSupport && TryGetGrpcMethod(activity, out var grpcMethod)) + { + AddGrpcAttributes(activity, grpcMethod, context); + } + if (activity.Status == ActivityStatusCode.Unset) { activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(activity.Kind, response.StatusCode)); @@ -327,27 +331,15 @@ static bool TryFetchException(object payload, out Exception exc) => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; } -#if !NETSTANDARD2_0 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryGetGrpcMethod(Activity activity, out string grpcMethod) { grpcMethod = GrpcTagHelper.GetGrpcMethodFromActivity(activity); return !string.IsNullOrEmpty(grpcMethod); } -#endif - - private string GetDisplayName(string httpMethod, string httpRoute = null) - { - var normalizedMethod = RequestMethodHelper.GetNormalizedHttpMethod(httpMethod); - return string.IsNullOrEmpty(httpRoute) - ? normalizedMethod - : $"{normalizedMethod} {httpRoute}"; - } - -#if !NETSTANDARD2_0 [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext context) + private static void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext context) { // The RPC semantic conventions indicate the span name // should not have a leading forward slash. @@ -389,5 +381,13 @@ private void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext } } } -#endif + + private static string GetDisplayName(string httpMethod, string httpRoute = null) + { + var normalizedMethod = RequestMethodHelper.GetNormalizedHttpMethod(httpMethod); + + return string.IsNullOrEmpty(httpRoute) + ? normalizedMethod + : $"{normalizedMethod} {httpRoute}"; + } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 4fe276ae2c..c2afcd02a8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -42,32 +42,12 @@ internal HttpInMetricsListener(string name) { } - public override void OnEventWritten(string name, object payload) - { - switch (name) - { - case OnUnhandledDiagnosticsExceptionEvent: - case OnUnhandledHostingExceptionEvent: - { - this.OnExceptionEventWritten(name, payload); - } - - break; - case OnStopEvent: - { - this.OnStopEventWritten(name, payload); - } - - break; - } - } - - public void OnExceptionEventWritten(string name, object payload) + public static void OnExceptionEventWritten(string name, object payload) { // We need to use reflection here as the payload type is not a defined public type. if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(this.OnExceptionEventWritten), HttpServerRequestDurationMetricName); + AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(OnExceptionEventWritten), HttpServerRequestDurationMetricName); return; } @@ -88,12 +68,12 @@ static bool TryFetchHttpContext(object payload, out HttpContext ctx) => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; } - public void OnStopEventWritten(string name, object payload) + public static void OnStopEventWritten(string name, object payload) { var context = payload as HttpContext; if (context == null) { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), EventName, HttpServerRequestDurationMetricName); + AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(OnStopEventWritten), HttpServerRequestDurationMetricName); return; } @@ -124,4 +104,24 @@ public void OnStopEventWritten(string name, object payload) // TODO: Follow up with .NET team if we can continue to rely on this behavior. HttpServerRequestDuration.Record(Activity.Current.Duration.TotalSeconds, tags); } + + public override void OnEventWritten(string name, object payload) + { + switch (name) + { + case OnUnhandledDiagnosticsExceptionEvent: + case OnUnhandledHostingExceptionEvent: + { + OnExceptionEventWritten(name, payload); + } + + break; + case OnStopEvent: + { + OnStopEventWritten(name, payload); + } + + break; + } + } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj index e3782aa277..c96ded5524 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj @@ -4,6 +4,7 @@ $(TargetFrameworksForAspNetCoreInstrumentation) ASP.NET Core instrumentation for OpenTelemetry .NET $(PackageTags);distributed-tracing;AspNetCore + Instrumentation.AspNetCore- true @@ -11,11 +12,13 @@ + + @@ -28,10 +31,11 @@ - + + diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md index 38269610e4..482aad252b 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md @@ -9,18 +9,13 @@ which instruments [ASP.NET Core](https://docs.microsoft.com/aspnet/core) and collect metrics and traces about incoming web requests. This instrumentation also collects traces from incoming gRPC requests using [Grpc.AspNetCore](https://www.nuget.org/packages/Grpc.AspNetCore). +Instrumentation support for gRPC server requests is supported via an +[experimental](#experimental-support-for-grpc-requests) feature flag. -**Note: This component is based on the OpenTelemetry semantic conventions for -[metrics](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md) -and -[traces](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md). -These conventions are -[Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md), -and hence, this package is a [pre-release](../../VERSIONING.md#pre-releases). -Until a [stable -version](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/telemetry-stability.md) -is released, there can be breaking changes. You can track the progress from -[milestones](https://github.com/open-telemetry/opentelemetry-dotnet/milestone/23).** +This component is based on the +[v1.23](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http) +of http semantic conventions. For details on the default set of attributes that +are added, checkout [Traces](#traces) and [Metrics](#metrics) sections below. ## Steps to enable OpenTelemetry.Instrumentation.AspNetCore @@ -31,7 +26,7 @@ Add a reference to the package. Also, add any other instrumentations & exporters you will need. ```shell -dotnet add package --prerelease OpenTelemetry.Instrumentation.AspNetCore +dotnet add package OpenTelemetry.Instrumentation.AspNetCore ``` ### Step 2: Enable ASP.NET Core Instrumentation at application startup @@ -66,6 +61,26 @@ public void ConfigureServices(IServiceCollection services) } ``` +Following list of attributes are added by default on activity. See +[http-spans](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-spans.md) +for more details about each individual attribute: + +* `error.type` +* `http.request.method` +* `http.request.method_original` +* `http.response.status_code` +* `http.route` +* `network.protocol.version` +* `user_agent.original` +* `server.address` +* `server.port` +* `url.path` +* `url.query` +* `url.scheme` + +[Enrich Api](#enrich) can be used if any additional attributes are +required on activity. + #### Metrics The following example demonstrates adding ASP.NET Core instrumentation with the @@ -88,6 +103,20 @@ public void ConfigureServices(IServiceCollection services) } ``` +Following list of attributes are added by default on +`http.server.request.duration` metric. See +[http-metrics](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-metrics.md) +for more details about each individual attribute. `.NET8.0` and above supports +additional metrics, see [list of metrics produced](#list-of-metrics-produced) for +more details. + +* `error.type` +* `http.response.status_code` +* `http.request.method` +* `http.route` +* `network.protocol.version` +* `url.scheme` + #### List of metrics produced When the application targets `.NET6.0` or `.NET7.0`, the instrumentation emits @@ -138,6 +167,8 @@ newer versions. ## Advanced configuration +### Tracing + This instrumentation can be configured to change the default behavior by using `AspNetCoreTraceInstrumentationOptions`, which allows adding [`Filter`](#filter), [`Enrich`](#enrich) as explained below. @@ -166,7 +197,7 @@ services.AddOpenTelemetry() .AddConsoleExporter()); ``` -### Filter +#### Filter This instrumentation by default collects all the incoming http requests. It allows filtering of requests by using the `Filter` function in @@ -194,7 +225,7 @@ instrumentation. OpenTelemetry has a concept of a [Sampler](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling), and the `Filter` option does the filtering *after* the Sampler is invoked. -### Enrich +#### Enrich This instrumentation library provides `EnrichWithHttpRequest`, `EnrichWithHttpResponse` and `EnrichWithException` options that can be used to @@ -231,13 +262,13 @@ is the general extensibility point to add additional properties to any activity. The `Enrich` option is specific to this instrumentation, and is provided to get access to `HttpRequest` and `HttpResponse`. -### RecordException +#### RecordException This instrumentation automatically sets Activity Status to Error if an unhandled exception is thrown. Additionally, `RecordException` feature may be turned on, to store the exception to the Activity itself as ActivityEvent. -## Activity Duration and http.server.request.duration metric calculation +## Activity duration and http.server.request.duration metric calculation `Activity.Duration` and `http.server.request.duration` values represents the time used to handle an inbound HTTP request as measured at the hosting layer of @@ -254,6 +285,31 @@ The time ends when: * All response data has been sent. * The context data structures for the request are being disposed. +## Experimental support for gRPC requests + +gRPC instrumentation can be enabled by setting +`OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` flag to +`True`. The flag can be set as an environment variable or via IConfiguration as +shown below. + +```csharp +var appBuilder = WebApplication.CreateBuilder(args); + +appBuilder.Configuration.AddInMemoryCollection( + new Dictionary + { + ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION"] = "true", + }); + +appBuilder.Services.AddOpenTelemetry() + .WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation()); +``` + + Semantic conventions for RPC are still + [experimental](https://github.com/open-telemetry/semantic-conventions/tree/main/docs/rpc) + and hence the instrumentation only offers it as an experimental feature. + ## Troubleshooting This component uses an diff --git a/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Shipped.txt index e69de29bb2..a082f2c5be 100644 --- a/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Shipped.txt @@ -0,0 +1,24 @@ +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithException.get -> System.Action +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithException.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage.get -> System.Action +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage.get -> System.Action +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest.get -> System.Action +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebResponse.get -> System.Action +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebResponse.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpRequestMessage.get -> System.Func +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpRequestMessage.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpWebRequest.get -> System.Func +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpWebRequest.set -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.HttpClientTraceInstrumentationOptions() -> void +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.RecordException.get -> bool +OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.RecordException.set -> void +OpenTelemetry.Metrics.HttpClientInstrumentationMeterProviderBuilderExtensions +OpenTelemetry.Trace.HttpClientInstrumentationTracerProviderBuilderExtensions +static OpenTelemetry.Metrics.HttpClientInstrumentationMeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Trace.HttpClientInstrumentationTracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder +static OpenTelemetry.Trace.HttpClientInstrumentationTracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureHttpClientTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder +static OpenTelemetry.Trace.HttpClientInstrumentationTracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureHttpClientTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Unshipped.txt index caa4690c8d..e69de29bb2 100644 --- a/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.Http/.publicApi/PublicAPI.Unshipped.txt @@ -1,24 +0,0 @@ -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithException.get -> System.Action -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithException.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage.get -> System.Action -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage.get -> System.Action -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest.get -> System.Action -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebResponse.get -> System.Action -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebResponse.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpRequestMessage.get -> System.Func -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpRequestMessage.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpWebRequest.get -> System.Func -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.FilterHttpWebRequest.set -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.HttpClientTraceInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.RecordException.get -> bool -OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.RecordException.set -> void -OpenTelemetry.Metrics.MeterProviderBuilderExtensions -OpenTelemetry.Trace.TracerProviderBuilderExtensions -static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureHttpClientTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureHttpClientTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index af38a5eab6..14886ecf83 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +## 1.7.0 + +Released 2023-Dec-13 + +## 1.6.0 - First stable release of this library + +Released 2023-Dec-13 + ## 1.6.0-rc.1 Released 2023-Dec-01 diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationMeterProviderBuilderExtensions.cs similarity index 94% rename from src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs rename to src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationMeterProviderBuilderExtensions.cs index e523518c3d..24b9524696 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationMeterProviderBuilderExtensions.cs @@ -15,7 +15,7 @@ namespace OpenTelemetry.Metrics; /// /// Extension methods to simplify registering of HttpClient instrumentation. /// -public static class MeterProviderBuilderExtensions +public static class HttpClientInstrumentationMeterProviderBuilderExtensions { /// /// Enables HttpClient instrumentation. diff --git a/src/OpenTelemetry.Instrumentation.Http/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs similarity index 98% rename from src/OpenTelemetry.Instrumentation.Http/TracerProviderBuilderExtensions.cs rename to src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs index 80855ed7f9..6015c183f2 100644 --- a/src/OpenTelemetry.Instrumentation.Http/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs @@ -12,7 +12,7 @@ namespace OpenTelemetry.Trace; /// /// Extension methods to simplify registering of HttpClient instrumentation. /// -public static class TracerProviderBuilderExtensions +public static class HttpClientInstrumentationTracerProviderBuilderExtensions { /// /// Enables HttpClient instrumentation. diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpRequestMessageContextPropagation.cs b/src/OpenTelemetry.Instrumentation.Http/HttpRequestMessageContextPropagation.cs index e1a02ef169..91d2a5c18a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpRequestMessageContextPropagation.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpRequestMessageContextPropagation.cs @@ -9,16 +9,6 @@ namespace OpenTelemetry.Instrumentation.Http; internal static class HttpRequestMessageContextPropagation { - internal static Func> HeaderValuesGetter => (request, name) => - { - if (request.Headers.TryGetValues(name, out var values)) - { - return values; - } - - return null; - }; - internal static Action HeaderValueSetter => (request, name, value) => { request.Headers.Remove(name); diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs index 496b739f57..c6a710e2dd 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs @@ -31,7 +31,7 @@ internal sealed class HttpHandlerMetricsDiagnosticListener : ListenerHandler private static readonly PropertyFetcher StopExceptionFetcher = new("Exception"); private static readonly PropertyFetcher RequestFetcher = new("Request"); #if NET6_0_OR_GREATER - private static readonly HttpRequestOptionsKey HttpRequestOptionsErrorKey = new HttpRequestOptionsKey(SemanticConventions.AttributeErrorType); + private static readonly HttpRequestOptionsKey HttpRequestOptionsErrorKey = new(SemanticConventions.AttributeErrorType); #endif public HttpHandlerMetricsDiagnosticListener(string name) @@ -39,35 +39,15 @@ public HttpHandlerMetricsDiagnosticListener(string name) { } - public override void OnEventWritten(string name, object payload) - { - if (name == OnUnhandledExceptionEvent) - { - this.OnExceptionEventWritten(Activity.Current, payload); - } - else if (name == OnStopEvent) - { - this.OnStopEventWritten(Activity.Current, payload); - } - } - - public void OnStopEventWritten(Activity activity, object payload) + public static void OnStopEventWritten(Activity activity, object payload) { if (TryFetchRequest(payload, out HttpRequestMessage request)) { // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-metrics.md TagList tags = default; - if (RequestMethodHelper.KnownMethods.TryGetValue(request.Method.Method, out var httpMethod)) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); - } - else - { - // Set to default "_OTHER" as per spec. - // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, "_OTHER")); - } + var httpMethod = RequestMethodHelper.GetNormalizedHttpMethod(request.Method.Method); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); tags.Add(new KeyValuePair(SemanticConventions.AttributeServerAddress, request.RequestUri.Host)); tags.Add(new KeyValuePair(SemanticConventions.AttributeUrlScheme, request.RequestUri.Scheme)); @@ -129,11 +109,11 @@ static bool TryFetchResponse(object payload, out HttpResponseMessage response) = StopResponseFetcher.TryFetch(payload, out response) && response != null; } - public void OnExceptionEventWritten(Activity activity, object payload) + public static void OnExceptionEventWritten(Activity activity, object payload) { if (!TryFetchException(payload, out Exception exc) || !TryFetchRequest(payload, out HttpRequestMessage request)) { - HttpInstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerMetricsDiagnosticListener), nameof(this.OnExceptionEventWritten)); + HttpInstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerMetricsDiagnosticListener), nameof(OnExceptionEventWritten)); return; } @@ -173,4 +153,16 @@ static bool TryFetchRequest(object payload, out HttpRequestMessage request) return true; } } + + public override void OnEventWritten(string name, object payload) + { + if (name == OnStopEvent) + { + OnStopEventWritten(Activity.Current, payload); + } + else if (name == OnUnhandledExceptionEvent) + { + OnExceptionEventWritten(Activity.Current, payload); + } + } } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs index 7b3b460c3a..82d7fd11bf 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs @@ -1,11 +1,6 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using System.Collections.Concurrent; -#if NETFRAMEWORK -using System.Net.Http; -#endif - namespace OpenTelemetry.Instrumentation.Http.Implementation; /// @@ -13,35 +8,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation; /// internal static class HttpTagHelper { - private static readonly ConcurrentDictionary MethodOperationNameCache = new(); - private static readonly ConcurrentDictionary HttpMethodOperationNameCache = new(); - private static readonly ConcurrentDictionary HttpMethodNameCache = new(); - - private static readonly Func ConvertMethodToOperationNameRef = ConvertMethodToOperationName; - private static readonly Func ConvertHttpMethodToOperationNameRef = ConvertHttpMethodToOperationName; - private static readonly Func ConvertHttpMethodToNameRef = ConvertHttpMethodToName; - - /// - /// Gets the OpenTelemetry standard name for an activity based on its Http method. - /// - /// Http method. - /// Activity name. - public static string GetOperationNameForHttpMethod(string method) => MethodOperationNameCache.GetOrAdd(method, ConvertMethodToOperationNameRef); - - /// - /// Gets the OpenTelemetry standard operation name for a span based on its . - /// - /// . - /// Span operation name. - public static string GetOperationNameForHttpMethod(HttpMethod method) => HttpMethodOperationNameCache.GetOrAdd(method, ConvertHttpMethodToOperationNameRef); - - /// - /// Gets the OpenTelemetry standard method name for a span based on its . - /// - /// . - /// Span method name. - public static string GetNameForHttpMethod(HttpMethod method) => HttpMethodNameCache.GetOrAdd(method, ConvertHttpMethodToNameRef); - /// /// Gets the OpenTelemetry standard uri tag value for a span based on its request . /// @@ -65,10 +31,4 @@ public static string GetUriTagValueFromRequestUri(Uri uri) (3, 0) => "3", _ => httpVersion.ToString(), }; - - private static string ConvertMethodToOperationName(string method) => $"HTTP {method}"; - - private static string ConvertHttpMethodToOperationName(HttpMethod method) => $"HTTP {method}"; - - private static string ConvertHttpMethodToName(HttpMethod method) => method.ToString(); } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 63d3755672..ea8e88abb5 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -425,16 +425,8 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC TagList tags = default; - if (RequestMethodHelper.KnownMethods.TryGetValue(request.Method, out var httpMethod)) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); - } - else - { - // Set to default "_OTHER" as per spec. - // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, "_OTHER")); - } + var httpMethod = RequestMethodHelper.GetNormalizedHttpMethod(request.Method); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); tags.Add(SemanticConventions.AttributeServerAddress, request.RequestUri.Host); tags.Add(SemanticConventions.AttributeUrlScheme, request.RequestUri.Scheme); diff --git a/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj b/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj index 32f80e31c2..55158028cd 100644 --- a/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj +++ b/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj @@ -4,6 +4,7 @@ $(TargetFrameworksForLibraries) Http instrumentation for OpenTelemetry .NET $(PackageTags);distributed-tracing + Instrumentation.Http- true diff --git a/src/OpenTelemetry.Instrumentation.Http/README.md b/src/OpenTelemetry.Instrumentation.Http/README.md index 4eb4e2adf7..3024c7ac80 100644 --- a/src/OpenTelemetry.Instrumentation.Http/README.md +++ b/src/OpenTelemetry.Instrumentation.Http/README.md @@ -11,18 +11,10 @@ and [System.Net.HttpWebRequest](https://docs.microsoft.com/dotnet/api/system.net.httpwebrequest) and collects metrics and traces about outgoing HTTP requests. -**Note: This component is based on the OpenTelemetry semantic conventions for -[metrics](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md) -and -[traces](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md). -These conventions are -[Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md), -and hence, this package is a [pre-release](../../VERSIONING.md#pre-releases). -Until a [stable -version](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/telemetry-stability.md) -is released, there can be [breaking changes](./CHANGELOG.md). You can track the -progress from -[milestones](https://github.com/open-telemetry/opentelemetry-dotnet/milestone/23).** +This component is based on the +[v1.23](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http) +of http semantic conventions. For details on the default set of attributes that +are added, checkout [Traces](#traces) and [Metrics](#metrics) sections below. ## Steps to enable OpenTelemetry.Instrumentation.Http @@ -33,7 +25,7 @@ Add a reference to the package. Also, add any other instrumentations & exporters you will need. ```shell -dotnet add package --prerelease OpenTelemetry.Instrumentation.Http +dotnet add package OpenTelemetry.Instrumentation.Http ``` ### Step 2: Enable HTTP Instrumentation at application startup @@ -65,6 +57,22 @@ public class Program } ``` +Following list of attributes are added by default on activity. See +[http-spans](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-spans.md) +for more details about each individual attribute: + +* `error.type` +* `http.request.method` +* `http.request.method_original` +* `http.response.status_code` +* `network.protocol.version` +* `server.address` +* `server.port` +* `url.full` + +[Enrich Api](#enrich-httpclient-api) can be used if any additional attributes are +required on activity. + #### Metrics The following example demonstrates adding `HttpClient` instrumentation with the @@ -97,6 +105,21 @@ Refer to this [example](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/src/OpenTelemetry.Instrumentation.AspNet/README.md) to see how to enable this instrumentation in an ASP.NET application. +Following list of attributes are added by default on +`http.client.request.duration` metric. See +[http-metrics](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-metrics.md) +for more details about each individual attribute. `.NET8.0` and above supports +additional metrics, see [list of metrics produced](#list-of-metrics-produced) for +more details. + +* `error.type` +* `http.request.method` +* `http.response.status_code` +* `network.protocol.version` +* `server.address` +* `server.port` +* `url.scheme` + #### List of metrics produced When the application targets `NETFRAMEWORK`, `.NET6.0` or `.NET7.0`, the @@ -147,6 +170,8 @@ newer versions. ## Advanced configuration +### Tracing + This instrumentation can be configured to change the default behavior by using `HttpClientTraceInstrumentationOptions`. It is important to note that there are differences between .NET Framework and newer .NET/.NET Core runtimes which @@ -155,9 +180,9 @@ govern what options are used. On .NET Framework, `HttpClient` uses the `HttpClient` API. As such, depending on the runtime, only one half of the "filter" & "enrich" options are used. -### .NET & .NET Core +#### .NET & .NET Core -#### Filter HttpClient API +##### Filter HttpClient API This instrumentation by default collects all the outgoing HTTP requests. It allows filtering of requests by using the `FilterHttpRequestMessage` function @@ -189,7 +214,7 @@ to this instrumentation. OpenTelemetry has a concept of a and the `FilterHttpRequestMessage` option does the filtering *after* the Sampler is invoked. -#### Enrich HttpClient API +##### Enrich HttpClient API This instrumentation library provides options that can be used to enrich the activity with additional information. These actions are called @@ -228,9 +253,9 @@ var tracerProvider = Sdk.CreateTracerProviderBuilder() .Build(); ``` -### .NET Framework +#### .NET Framework -#### Filter HttpWebRequest API +##### Filter HttpWebRequest API This instrumentation by default collects all the outgoing HTTP requests. It allows filtering of requests by using the `FilterHttpWebRequest` function @@ -262,7 +287,7 @@ this instrumentation. OpenTelemetry has a concept of a and the `FilterHttpWebRequest` option does the filtering *after* the Sampler is invoked. -#### Enrich HttpWebRequest API +##### Enrich HttpWebRequest API This instrumentation library provides options that can be used to enrich the activity with additional information. These actions are called @@ -306,13 +331,13 @@ general extensibility point to add additional properties to any activity. The `Enrich` option is specific to this instrumentation, and is provided to get access to raw request, response, and exception objects. -### RecordException +#### RecordException This instrumentation automatically sets Activity Status to Error if the Http StatusCode is >= 400. Additionally, `RecordException` feature may be turned on, to store the exception to the Activity itself as ActivityEvent. -## Activity Duration and http.client.request.duration metric calculation +## Activity duration and http.client.request.duration metric calculation `Activity.Duration` and `http.client.request.duration` values represents the time the underlying client handler takes to complete the request. Completing the diff --git a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md index f540df67e0..577ddc62a9 100644 --- a/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md +++ b/src/OpenTelemetry.Shims.OpenTracing/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.7.0-beta.1 + +Released 2023-Dec-08 + * Remove obsolete `TracerShim(Tracer, TextMapPropagator)` constructor. Use `TracerShim(TracerProvider)` or `TracerShim(TracerProvider, TextMapPropagator)` constructors. diff --git a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Shipped.txt index 77ed02bc8c..ae4fe17043 100644 --- a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Shipped.txt @@ -1,28 +1,5 @@ #nullable enable ~OpenTelemetry.Batch.GetEnumerator() -> OpenTelemetry.Batch.Enumerator -OpenTelemetry.Metrics.BaseExportingMetricReader.BaseExportingMetricReader(OpenTelemetry.BaseExporter! exporter) -> void -OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.get -> double[]? -OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.set -> void -OpenTelemetry.Metrics.IPullMetricExporter.Collect.get -> System.Func? -OpenTelemetry.Metrics.IPullMetricExporter.Collect.set -> void -OpenTelemetry.Metrics.Metric.Description.get -> string! -OpenTelemetry.Metrics.Metric.MeterName.get -> string! -OpenTelemetry.Metrics.Metric.MeterVersion.get -> string! -OpenTelemetry.Metrics.Metric.Name.get -> string! -OpenTelemetry.Metrics.Metric.Unit.get -> string! -OpenTelemetry.Metrics.MetricStreamConfiguration.Description.get -> string? -OpenTelemetry.Metrics.MetricStreamConfiguration.Description.set -> void -OpenTelemetry.Metrics.MetricStreamConfiguration.Name.get -> string? -OpenTelemetry.Metrics.MetricStreamConfiguration.Name.set -> void -OpenTelemetry.Metrics.MetricStreamConfiguration.TagKeys.get -> string![]? -OpenTelemetry.Metrics.MetricStreamConfiguration.TagKeys.set -> void -OpenTelemetry.Metrics.PeriodicExportingMetricReader.PeriodicExportingMetricReader(OpenTelemetry.BaseExporter! exporter, int exportIntervalMilliseconds = 60000, int exportTimeoutMilliseconds = 30000) -> void -override OpenTelemetry.Metrics.MeterProviderBuilderBase.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Metrics.MeterProviderBuilder! -override OpenTelemetry.Trace.TracerProviderBuilderBase.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Trace.TracerProviderBuilder! -readonly OpenTelemetry.Metrics.BaseExportingMetricReader.exporter -> OpenTelemetry.BaseExporter! -static OpenTelemetry.Metrics.MeterProviderExtensions.ForceFlush(this OpenTelemetry.Metrics.MeterProvider! provider, int timeoutMilliseconds = -1) -> bool -static OpenTelemetry.Metrics.MeterProviderExtensions.Shutdown(this OpenTelemetry.Metrics.MeterProvider! provider, int timeoutMilliseconds = -1) -> bool -static OpenTelemetry.Metrics.MetricStreamConfiguration.Drop.get -> OpenTelemetry.Metrics.MetricStreamConfiguration! abstract OpenTelemetry.BaseExporter.Export(in OpenTelemetry.Batch batch) -> OpenTelemetry.ExportResult abstract OpenTelemetry.BaseExportProcessor.OnExport(T! data) -> void abstract OpenTelemetry.Trace.Sampler.ShouldSample(in OpenTelemetry.Trace.SamplingParameters samplingParameters) -> OpenTelemetry.Trace.SamplingResult @@ -150,8 +127,11 @@ OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.set OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.get -> int OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.set -> void OpenTelemetry.Metrics.BaseExportingMetricReader +OpenTelemetry.Metrics.BaseExportingMetricReader.BaseExportingMetricReader(OpenTelemetry.BaseExporter! exporter) -> void OpenTelemetry.Metrics.BaseExportingMetricReader.SupportedExportModes.get -> OpenTelemetry.Metrics.ExportModes OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration +OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.get -> double[]? +OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.set -> void OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.ExplicitBucketHistogramConfiguration() -> void OpenTelemetry.Metrics.ExponentialHistogramBuckets OpenTelemetry.Metrics.ExponentialHistogramBuckets.Enumerator @@ -185,15 +165,23 @@ OpenTelemetry.Metrics.HistogramConfiguration.HistogramConfiguration() -> void OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.get -> bool OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.set -> void OpenTelemetry.Metrics.IPullMetricExporter +OpenTelemetry.Metrics.IPullMetricExporter.Collect.get -> System.Func? +OpenTelemetry.Metrics.IPullMetricExporter.Collect.set -> void OpenTelemetry.Metrics.MeterProviderBuilderBase OpenTelemetry.Metrics.MeterProviderBuilderBase.Build() -> OpenTelemetry.Metrics.MeterProvider! OpenTelemetry.Metrics.MeterProviderBuilderBase.MeterProviderBuilderBase() -> void OpenTelemetry.Metrics.MeterProviderBuilderExtensions OpenTelemetry.Metrics.MeterProviderExtensions OpenTelemetry.Metrics.Metric +OpenTelemetry.Metrics.Metric.Description.get -> string! OpenTelemetry.Metrics.Metric.GetMetricPoints() -> OpenTelemetry.Metrics.MetricPointsAccessor +OpenTelemetry.Metrics.Metric.MeterName.get -> string! +OpenTelemetry.Metrics.Metric.MeterTags.get -> System.Collections.Generic.IEnumerable>? +OpenTelemetry.Metrics.Metric.MeterVersion.get -> string! OpenTelemetry.Metrics.Metric.MetricType.get -> OpenTelemetry.Metrics.MetricType +OpenTelemetry.Metrics.Metric.Name.get -> string! OpenTelemetry.Metrics.Metric.Temporality.get -> OpenTelemetry.Metrics.AggregationTemporality +OpenTelemetry.Metrics.Metric.Unit.get -> string! OpenTelemetry.Metrics.MetricPoint OpenTelemetry.Metrics.MetricPoint.EndTime.get -> System.DateTimeOffset OpenTelemetry.Metrics.MetricPoint.GetExponentialHistogramData() -> OpenTelemetry.Metrics.ExponentialHistogramData! @@ -232,7 +220,13 @@ OpenTelemetry.Metrics.MetricReaderTemporalityPreference OpenTelemetry.Metrics.MetricReaderTemporalityPreference.Cumulative = 1 -> OpenTelemetry.Metrics.MetricReaderTemporalityPreference OpenTelemetry.Metrics.MetricReaderTemporalityPreference.Delta = 2 -> OpenTelemetry.Metrics.MetricReaderTemporalityPreference OpenTelemetry.Metrics.MetricStreamConfiguration +OpenTelemetry.Metrics.MetricStreamConfiguration.Description.get -> string? +OpenTelemetry.Metrics.MetricStreamConfiguration.Description.set -> void OpenTelemetry.Metrics.MetricStreamConfiguration.MetricStreamConfiguration() -> void +OpenTelemetry.Metrics.MetricStreamConfiguration.Name.get -> string? +OpenTelemetry.Metrics.MetricStreamConfiguration.Name.set -> void +OpenTelemetry.Metrics.MetricStreamConfiguration.TagKeys.get -> string![]? +OpenTelemetry.Metrics.MetricStreamConfiguration.TagKeys.set -> void OpenTelemetry.Metrics.MetricType OpenTelemetry.Metrics.MetricType.DoubleGauge = 45 -> OpenTelemetry.Metrics.MetricType OpenTelemetry.Metrics.MetricType.DoubleSum = 29 -> OpenTelemetry.Metrics.MetricType @@ -244,6 +238,7 @@ OpenTelemetry.Metrics.MetricType.LongSum = 26 -> OpenTelemetry.Metrics.MetricTyp OpenTelemetry.Metrics.MetricType.LongSumNonMonotonic = 138 -> OpenTelemetry.Metrics.MetricType OpenTelemetry.Metrics.MetricTypeExtensions OpenTelemetry.Metrics.PeriodicExportingMetricReader +OpenTelemetry.Metrics.PeriodicExportingMetricReader.PeriodicExportingMetricReader(OpenTelemetry.BaseExporter! exporter, int exportIntervalMilliseconds = 60000, int exportTimeoutMilliseconds = 30000) -> void OpenTelemetry.Metrics.PeriodicExportingMetricReaderOptions OpenTelemetry.Metrics.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds.get -> int? OpenTelemetry.Metrics.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds.set -> void @@ -346,6 +341,7 @@ override OpenTelemetry.Logs.OpenTelemetryLoggerProvider.Dispose(bool disposing) override OpenTelemetry.Metrics.BaseExportingMetricReader.Dispose(bool disposing) -> void override OpenTelemetry.Metrics.BaseExportingMetricReader.OnCollect(int timeoutMilliseconds) -> bool override OpenTelemetry.Metrics.BaseExportingMetricReader.OnShutdown(int timeoutMilliseconds) -> bool +override OpenTelemetry.Metrics.MeterProviderBuilderBase.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Metrics.MeterProviderBuilder! override OpenTelemetry.Metrics.MeterProviderBuilderBase.AddMeter(params string![]! names) -> OpenTelemetry.Metrics.MeterProviderBuilder! override OpenTelemetry.Metrics.PeriodicExportingMetricReader.Dispose(bool disposing) -> void override OpenTelemetry.Metrics.PeriodicExportingMetricReader.OnShutdown(int timeoutMilliseconds) -> bool @@ -357,10 +353,12 @@ override OpenTelemetry.Trace.ParentBasedSampler.ShouldSample(in OpenTelemetry.Tr override OpenTelemetry.Trace.SamplingResult.Equals(object? obj) -> bool override OpenTelemetry.Trace.SamplingResult.GetHashCode() -> int override OpenTelemetry.Trace.TraceIdRatioBasedSampler.ShouldSample(in OpenTelemetry.Trace.SamplingParameters samplingParameters) -> OpenTelemetry.Trace.SamplingResult +override OpenTelemetry.Trace.TracerProviderBuilderBase.AddInstrumentation(System.Func! instrumentationFactory) -> OpenTelemetry.Trace.TracerProviderBuilder! override OpenTelemetry.Trace.TracerProviderBuilderBase.AddLegacySource(string! operationName) -> OpenTelemetry.Trace.TracerProviderBuilder! override OpenTelemetry.Trace.TracerProviderBuilderBase.AddSource(params string![]! names) -> OpenTelemetry.Trace.TracerProviderBuilder! override sealed OpenTelemetry.BaseExportProcessor.OnStart(T! data) -> void readonly OpenTelemetry.BaseExportProcessor.exporter -> OpenTelemetry.BaseExporter! +readonly OpenTelemetry.Metrics.BaseExportingMetricReader.exporter -> OpenTelemetry.BaseExporter! static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder! static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder! static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddReader(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, OpenTelemetry.Metrics.MetricReader! reader) -> OpenTelemetry.Metrics.MeterProviderBuilder! @@ -374,6 +372,9 @@ static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(th static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.SetMaxMetricPointsPerMetricStream(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, int maxMetricPointsPerMetricStream) -> OpenTelemetry.Metrics.MeterProviderBuilder! static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.SetMaxMetricStreams(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, int maxMetricStreams) -> OpenTelemetry.Metrics.MeterProviderBuilder! static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.SetResourceBuilder(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, OpenTelemetry.Resources.ResourceBuilder! resourceBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder! +static OpenTelemetry.Metrics.MeterProviderExtensions.ForceFlush(this OpenTelemetry.Metrics.MeterProvider! provider, int timeoutMilliseconds = -1) -> bool +static OpenTelemetry.Metrics.MeterProviderExtensions.Shutdown(this OpenTelemetry.Metrics.MeterProvider! provider, int timeoutMilliseconds = -1) -> bool +static OpenTelemetry.Metrics.MetricStreamConfiguration.Drop.get -> OpenTelemetry.Metrics.MetricStreamConfiguration! static OpenTelemetry.Metrics.MetricTypeExtensions.IsDouble(this OpenTelemetry.Metrics.MetricType self) -> bool static OpenTelemetry.Metrics.MetricTypeExtensions.IsGauge(this OpenTelemetry.Metrics.MetricType self) -> bool static OpenTelemetry.Metrics.MetricTypeExtensions.IsHistogram(this OpenTelemetry.Metrics.MetricType self) -> bool diff --git a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt index 01e38e9263..e69de29bb2 100644 --- a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1 +0,0 @@ -OpenTelemetry.Metrics.Metric.MeterTags.get -> System.Collections.Generic.IEnumerable>? diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 8c657465f8..f760d47565 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,6 +2,17 @@ ## Unreleased +* Fixed an issue where `LogRecord.Attributes` (or `LogRecord.StateValues` alias) + could become out of sync with `LogRecord.State` if either is set directly via + the public setters. This was done to further mitigate issues introduced in + 1.5.0 causing attributes added using custom processor(s) to be missing after + upgrading. For details see: + [#5169](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5169) + +## 1.7.0 + +Released 2023-Dec-08 + ## 1.7.0-rc.1 Released 2023-Nov-29 diff --git a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs index ba9544d39c..3d14dc16e0 100644 --- a/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs +++ b/src/OpenTelemetry/Internal/SelfDiagnosticsConfigRefresher.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using System.IO.MemoryMappedFiles; -using System.Text; namespace OpenTelemetry.Internal; @@ -18,7 +17,7 @@ namespace OpenTelemetry.Internal; /// internal class SelfDiagnosticsConfigRefresher : IDisposable { - public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("If you are seeing this message, it means that the OpenTelemetry SDK has successfully created the log file used to write self-diagnostic logs. This file will be appended with logs as they appear. If you do not see any logs following this line, it means no logs of the configured LogLevel is occurring. You may change the LogLevel to show lower log levels, so that logs of lower severities will be shown.\n"); + public static readonly byte[] MessageOnNewFile = "If you are seeing this message, it means that the OpenTelemetry SDK has successfully created the log file used to write self-diagnostic logs. This file will be appended with logs as they appear. If you do not see any logs following this line, it means no logs of the configured LogLevel is occurring. You may change the LogLevel to show lower log levels, so that logs of lower severities will be shown.\n"u8.ToArray(); private const int ConfigurationUpdatePeriodMilliSeconds = 10000; diff --git a/src/OpenTelemetry/Logs/Builder/LoggerProviderBuilderExtensions.cs b/src/OpenTelemetry/Logs/Builder/LoggerProviderBuilderExtensions.cs index 4ea08a0ff9..3fad4d173d 100644 --- a/src/OpenTelemetry/Logs/Builder/LoggerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry/Logs/Builder/LoggerProviderBuilderExtensions.cs @@ -15,6 +15,9 @@ namespace OpenTelemetry.Logs; /// Contains extension methods for the class. /// #if EXPOSE_EXPERIMENTAL_FEATURES +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else internal diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs index 621a66de30..acdc08f4fd 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs @@ -79,7 +79,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except LogRecordData.SetActivityContext(ref data, activity); - var attributes = record.Attributes = + var attributes = record.AttributeData = ProcessState(record, ref iloggerData, in state, this.options.IncludeAttributes, this.options.ParseStateValues); if (!TryGetOriginalFormatFromAttributes(attributes, out var originalFormat)) @@ -133,7 +133,7 @@ internal static void SetLogRecordSeverityFields(ref LogRecordData logRecordData, } } - private static IReadOnlyList>? ProcessState( + internal static IReadOnlyList>? ProcessState( LogRecord logRecord, ref LogRecord.LogRecordILoggerData iLoggerData, in TState state, diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs index a3512678bd..f81045c00c 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs @@ -76,6 +76,9 @@ public static ILoggingBuilder AddOpenTelemetry( /// /// The to use. /// The supplied for call chaining. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -90,7 +93,7 @@ public static ILoggingBuilder AddOpenTelemetry( /// The supplied for call chaining. internal #endif - static ILoggingBuilder UseOpenTelemetry( + static ILoggingBuilder UseOpenTelemetry( this ILoggingBuilder builder) => AddOpenTelemetryInternal(builder, configureBuilder: null, configureOptions: null); @@ -102,6 +105,9 @@ static ILoggingBuilder UseOpenTelemetry( /// The to use. /// Optional configuration action. /// The supplied for call chaining. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -113,7 +119,7 @@ static ILoggingBuilder UseOpenTelemetry( /// The supplied for call chaining. internal #endif - static ILoggingBuilder UseOpenTelemetry( + static ILoggingBuilder UseOpenTelemetry( this ILoggingBuilder builder, Action configure) { @@ -131,6 +137,9 @@ static ILoggingBuilder UseOpenTelemetry( /// Optional configuration action. /// Optional configuration action. /// The supplied for call chaining. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -143,7 +152,7 @@ static ILoggingBuilder UseOpenTelemetry( /// The supplied for call chaining. internal #endif - static ILoggingBuilder UseOpenTelemetry( + static ILoggingBuilder UseOpenTelemetry( this ILoggingBuilder builder, Action? configureBuilder, Action? configureOptions) diff --git a/src/OpenTelemetry/Logs/LogRecord.cs b/src/OpenTelemetry/Logs/LogRecord.cs index 9344b56d09..877ab83d1d 100644 --- a/src/OpenTelemetry/Logs/LogRecord.cs +++ b/src/OpenTelemetry/Logs/LogRecord.cs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; using OpenTelemetry.Internal; @@ -15,6 +18,7 @@ public sealed class LogRecord { internal LogRecordData Data; internal LogRecordILoggerData ILoggerData; + internal IReadOnlyList>? AttributeData; internal List>? AttributeStorage; internal List? ScopeStorage; internal int PoolReferenceCount = int.MaxValue; @@ -72,7 +76,7 @@ internal LogRecord( this.Data.Body = template; } - this.Attributes = stateValues; + this.AttributeData = stateValues; } } @@ -225,13 +229,30 @@ public string? Body /// through . /// Set to when is enabled. + /// are automatically updated if is set directly. /// /// [Obsolete("State cannot be accessed safely outside of an ILogger.Log call stack. Use Attributes instead to safely access the data attached to a LogRecord. State will be removed in a future version.")] public object? State { get => this.ILoggerData.State; - set => this.ILoggerData.State = value; + set + { + if (ReferenceEquals(this.ILoggerData.State, value)) + { + return; + } + + if (this.AttributeData is not null) + { + this.AttributeData = OpenTelemetryLogger.ProcessState(this, ref this.ILoggerData, value, includeAttributes: true, parseStateValues: false); + } + else + { + this.ILoggerData.State = value; + } + } } /// @@ -249,15 +270,37 @@ public object? State /// Gets or sets the attributes attached to the log. /// /// - /// Note: Set when is enabled and - /// log record state implements or + /// Set when is enabled and log + /// record state implements or of s /// (where TKey is string and TValue is object) or is enabled - /// otherwise . + /// otherwise . + /// is automatically updated if are set directly. + /// /// - public IReadOnlyList>? Attributes { get; set; } + public IReadOnlyList>? Attributes + { + get => this.AttributeData; + set + { + if (ReferenceEquals(this.AttributeData, value)) + { + return; + } + + if (this.ILoggerData.State is not null) + { + this.ILoggerData.State = value; + } + + this.AttributeData = value; + } + } /// /// Gets or sets the log . @@ -277,6 +320,9 @@ public Exception? Exception /// known at the source. /// /// +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -296,6 +342,9 @@ public Exception? Exception /// Gets or sets the log . /// /// +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -314,6 +363,9 @@ public Exception? Exception /// Gets the which emitted the . /// /// +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public Logger? Logger { get; internal set; } #else /// @@ -399,7 +451,7 @@ internal LogRecord Copy() { Data = this.Data, ILoggerData = this.ILoggerData.Copy(), - Attributes = this.Attributes == null ? null : new List>(this.Attributes), + AttributeData = this.AttributeData is null ? null : new List>(this.AttributeData), Logger = this.Logger, }; } @@ -410,7 +462,7 @@ internal LogRecord Copy() /// private void BufferLogAttributes() { - var attributes = this.Attributes; + var attributes = this.AttributeData; if (attributes == null || attributes == this.AttributeStorage) { return; @@ -425,7 +477,7 @@ private void BufferLogAttributes() // https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905. attributeStorage.AddRange(attributes); - this.Attributes = attributeStorage; + this.AttributeData = attributeStorage; } /// diff --git a/src/OpenTelemetry/Logs/LoggerProviderExtensions.cs b/src/OpenTelemetry/Logs/LoggerProviderExtensions.cs index 98dcbbf0d5..66dbac5ad4 100644 --- a/src/OpenTelemetry/Logs/LoggerProviderExtensions.cs +++ b/src/OpenTelemetry/Logs/LoggerProviderExtensions.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using OpenTelemetry.Internal; namespace OpenTelemetry.Logs; @@ -9,6 +12,9 @@ namespace OpenTelemetry.Logs; /// Contains extension methods for the class. /// #if EXPOSE_EXPERIMENTAL_FEATURES +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else internal diff --git a/src/OpenTelemetry/Logs/LoggerSdk.cs b/src/OpenTelemetry/Logs/LoggerSdk.cs index 85f4847369..a0bc47300d 100644 --- a/src/OpenTelemetry/Logs/LoggerSdk.cs +++ b/src/OpenTelemetry/Logs/LoggerSdk.cs @@ -38,7 +38,7 @@ public override void EmitLog(in LogRecordData data, in LogRecordAttributeList at logRecord.Logger = this; - logRecord.Attributes = attributes.Export(ref logRecord.AttributeStorage); + logRecord.AttributeData = attributes.Export(ref logRecord.AttributeStorage); processor.OnEnd(logRecord); diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs index 03c3d48eb1..11844d3e7b 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs @@ -323,6 +323,9 @@ public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder /// . /// ExemplarFilter to use. /// The supplied for chaining. +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -334,7 +337,7 @@ public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder /// The supplied for chaining. internal #endif - static MeterProviderBuilder SetExemplarFilter(this MeterProviderBuilder meterProviderBuilder, ExemplarFilter exemplarFilter) + static MeterProviderBuilder SetExemplarFilter(this MeterProviderBuilder meterProviderBuilder, ExemplarFilter exemplarFilter) { Guard.ThrowIfNull(exemplarFilter); diff --git a/src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs b/src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs index 8ab9f471fd..b6c7973b9d 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs @@ -1,6 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Metrics; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -9,6 +14,9 @@ namespace OpenTelemetry.Metrics; /// Using this ExemplarFilter is as good as disabling Exemplar feature. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -17,7 +25,7 @@ namespace OpenTelemetry.Metrics; /// internal #endif - sealed class AlwaysOffExemplarFilter : ExemplarFilter +sealed class AlwaysOffExemplarFilter : ExemplarFilter { /// public override bool ShouldSample(long value, ReadOnlySpan> tags) diff --git a/src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs b/src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs index 49f4bfb590..7be5d04db0 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs @@ -1,6 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Metrics; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -8,6 +13,9 @@ namespace OpenTelemetry.Metrics; /// An ExemplarFilter which makes all measurements eligible for being an Exemplar. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs b/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs index 5efd77a59e..d635a1a4ad 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs @@ -1,6 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + using System.Diagnostics; namespace OpenTelemetry.Metrics; @@ -10,6 +15,9 @@ namespace OpenTelemetry.Metrics; /// Represents an Exemplar data. /// /// WARNING: This is an experimental API which might change or be removed in the future. Use at your own risk. +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry/Metrics/Exemplar/ExemplarFilter.cs b/src/OpenTelemetry/Metrics/Exemplar/ExemplarFilter.cs index ca2617d22b..3af62b66c6 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/ExemplarFilter.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/ExemplarFilter.cs @@ -1,6 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + namespace OpenTelemetry.Metrics; #if EXPOSE_EXPERIMENTAL_FEATURES @@ -8,6 +13,9 @@ namespace OpenTelemetry.Metrics; /// The base class for defining Exemplar Filter. /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs b/src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs index 8fcf364f41..6d176f8a16 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs @@ -1,6 +1,11 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using OpenTelemetry.Internal; +#endif + using System.Diagnostics; namespace OpenTelemetry.Metrics; @@ -11,6 +16,9 @@ namespace OpenTelemetry.Metrics; /// which are recorded in the context of a sampled parent activity (span). /// /// +#if NET8_0_OR_GREATER +[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// diff --git a/src/OpenTelemetry/Sdk.cs b/src/OpenTelemetry/Sdk.cs index 7e2553e8cc..15df83f299 100644 --- a/src/OpenTelemetry/Sdk.cs +++ b/src/OpenTelemetry/Sdk.cs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; +#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Reflection; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Internal; @@ -92,6 +95,9 @@ public static TracerProviderBuilder CreateTracerProviderBuilder() /// WARNING: This is an experimental API which might change or be removed in the future. Use at your own risk. /// instance, which is used /// to build a . +#if NET8_0_OR_GREATER + [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] +#endif public #else /// @@ -105,7 +111,7 @@ public static TracerProviderBuilder CreateTracerProviderBuilder() /// to build a . internal #endif - static LoggerProviderBuilder CreateLoggerProviderBuilder() + static LoggerProviderBuilder CreateLoggerProviderBuilder() { return new LoggerProviderBuilderBase(); } diff --git a/src/Shared/DiagnosticDefinitions.cs b/src/Shared/DiagnosticDefinitions.cs new file mode 100644 index 0000000000..8d2bf49723 --- /dev/null +++ b/src/Shared/DiagnosticDefinitions.cs @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +namespace OpenTelemetry.Internal; + +internal static class DiagnosticDefinitions +{ + public const string ExperimentalApiUrlFormat = "https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/diagnostics/experimental-apis/README.md#{0}"; + + public const string LoggerProviderExperimentalApi = "OTEL1000"; + public const string LogsBridgeExperimentalApi = "OTEL1001"; + public const string ExemplarExperimentalApi = "OTEL1002"; +} diff --git a/test/Benchmarks/Helper/ActivityCreationScenarios.cs b/test/Benchmarks/Helper/ActivityCreationScenarios.cs index 6b1b9f2a1b..48c67b4c3d 100644 --- a/test/Benchmarks/Helper/ActivityCreationScenarios.cs +++ b/test/Benchmarks/Helper/ActivityCreationScenarios.cs @@ -13,30 +13,31 @@ public static void CreateActivity(ActivitySource source) activity?.Stop(); } - public static void CreateActivityWithKind(ActivitySource source) - { - using var activity = source.StartActivity("name", ActivityKind.Client); - activity?.Stop(); - } - public static void CreateActivityFromParentContext(ActivitySource source, ActivityContext parentCtx) { using var activity = source.StartActivity("name", ActivityKind.Internal, parentCtx); activity?.Stop(); } - public static void CreateActivityFromParentId(ActivitySource source, string parentId) + public static void CreateActivityWithSetTags(ActivitySource source) { - using var activity = source.StartActivity("name", ActivityKind.Internal, parentId); + using var activity = source.StartActivity("name"); + activity?.SetTag("tag1", "string"); + activity?.SetTag("tag2", 1); + activity?.SetTag("tag3", true); + activity?.SetTag("tag4", "string-again"); + activity?.SetTag("tag5", "string-more"); activity?.Stop(); } - public static void CreateActivityWithAttributes(ActivitySource source) + public static void CreateActivityWithAddTags(ActivitySource source) { using var activity = source.StartActivity("name"); - activity?.SetTag("tag1", "string"); - activity?.SetTag("tag2", 1); - activity?.SetTag("tag3", true); + activity?.AddTag("tag1", "string"); + activity?.AddTag("tag2", 1); + activity?.AddTag("tag3", true); + activity?.AddTag("tag4", "string-again"); + activity?.AddTag("tag5", "string-more"); activity?.Stop(); } } diff --git a/test/Benchmarks/Trace/ActivityCreationBenchmarks.cs b/test/Benchmarks/Trace/ActivityCreationBenchmarks.cs index b7c30f1212..447e550bd8 100644 --- a/test/Benchmarks/Trace/ActivityCreationBenchmarks.cs +++ b/test/Benchmarks/Trace/ActivityCreationBenchmarks.cs @@ -8,20 +8,19 @@ using OpenTelemetry.Trace; /* -BenchmarkDotNet v0.13.10, Windows 11 (10.0.23424.1000) -Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores +BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2861) +11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores .NET SDK 8.0.100 [Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 -| Method | Mean | Error | StdDev | Gen0 | Allocated | -|----------------------------------------------- |----------:|---------:|---------:|-------:|----------:| -| CreateActivity_NoopProcessor | 307.12 ns | 5.769 ns | 6.172 ns | 0.0663 | 416 B | -| CreateActivity_WithParentContext_NoopProcessor | 75.18 ns | 0.399 ns | 0.354 ns | - | - | -| CreateActivity_WithParentId_NoopProcessor | 156.52 ns | 1.609 ns | 1.426 ns | 0.0229 | 144 B | -| CreateActivity_WithAttributes_NoopProcessor | 372.34 ns | 6.215 ns | 4.852 ns | 0.0992 | 624 B | -| CreateActivity_WithKind_NoopProcessor | 302.24 ns | 5.859 ns | 8.402 ns | 0.0663 | 416 B | +| Method | Mean | Error | StdDev | Median | +|----------------------------------------------- |----------:|---------:|----------:|----------:| +| CreateActivity_NoopProcessor | 247.22 ns | 4.977 ns | 13.198 ns | 240.34 ns | +| CreateActivity_WithParentContext_NoopProcessor | 55.17 ns | 1.131 ns | 1.111 ns | 54.98 ns | +| CreateActivity_WithSetTags_NoopProcessor | 375.2 ns | 7.52 ns | 18.44 ns | 370.4 ns | +| CreateActivity_WithAddTags_NoopProcessor | 340.9 ns | 6.27 ns | 12.81 ns | 336.1 ns | */ namespace Benchmarks.Trace; @@ -30,7 +29,6 @@ public class ActivityCreationBenchmarks { private readonly ActivitySource benchmarkSource = new("Benchmark"); private readonly ActivityContext parentCtx = new(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None); - private readonly string parentId = $"00-{ActivityTraceId.CreateRandom()}.{ActivitySpanId.CreateRandom()}.00"; private TracerProvider tracerProvider; [GlobalSetup] @@ -38,6 +36,7 @@ public void GlobalSetup() { this.tracerProvider = Sdk.CreateTracerProviderBuilder() .AddSource("BenchMark") + .AddProcessor(new NoopActivityProcessor()) .Build(); } @@ -55,11 +54,12 @@ public void GlobalCleanup() public void CreateActivity_WithParentContext_NoopProcessor() => ActivityCreationScenarios.CreateActivityFromParentContext(this.benchmarkSource, this.parentCtx); [Benchmark] - public void CreateActivity_WithParentId_NoopProcessor() => ActivityCreationScenarios.CreateActivityFromParentId(this.benchmarkSource, this.parentId); + public void CreateActivity_WithSetTags_NoopProcessor() => ActivityCreationScenarios.CreateActivityWithSetTags(this.benchmarkSource); [Benchmark] - public void CreateActivity_WithAttributes_NoopProcessor() => ActivityCreationScenarios.CreateActivityWithAttributes(this.benchmarkSource); + public void CreateActivity_WithAddTags_NoopProcessor() => ActivityCreationScenarios.CreateActivityWithAddTags(this.benchmarkSource); - [Benchmark] - public void CreateActivity_WithKind_NoopProcessor() => ActivityCreationScenarios.CreateActivityWithKind(this.benchmarkSource); + internal class NoopActivityProcessor : BaseProcessor + { + } } diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index 8f71478088..7e1a6c80ba 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -19,6 +19,8 @@ namespace OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests; public sealed class PrometheusExporterMiddlewareTests { + private const string MeterVersion = "1.0.1"; + private static readonly string MeterName = Utils.GetCurrentMethodName(); [Fact] @@ -281,7 +283,7 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( new KeyValuePair("key2", "value2"), }; - using var meter = new Meter(MeterName); + using var meter = new Meter(MeterName, MeterVersion); var beginTimestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); @@ -320,11 +322,14 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string content = await response.Content.ReadAsStringAsync(); string expected = requestOpenMetrics - ? "# TYPE counter_double_total counter\n" - + "counter_double_total{key1='value1',key2='value2'} 101.17 (\\d+\\.\\d{3})\n" + ? "# TYPE otel_scope_info info\n" + + "# HELP otel_scope_info Scope metadata\n" + + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + + "# TYPE counter_double_total counter\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + "counter_double_total{key1='value1',key2='value2'} 101.17 (\\d+)\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; var matches = Regex.Matches(content, ("^" + expected + "$").Replace('\'', '"')); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index fc0f7d0fb7..2d0271d84e 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -14,7 +14,9 @@ namespace OpenTelemetry.Exporter.Prometheus.Tests; public class PrometheusHttpListenerTests { - private readonly string meterName = Utils.GetCurrentMethodName(); + private const string MeterVersion = "1.0.1"; + + private static readonly string MeterName = Utils.GetCurrentMethodName(); [Theory] [InlineData("http://+:9464")] @@ -99,7 +101,7 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri string address = null; MeterProvider provider = null; - using var meter = new Meter(this.meterName); + using var meter = new Meter(MeterName, MeterVersion); while (retryAttempts-- != 0) { @@ -110,7 +112,10 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri { provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) - .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { address }) + .AddPrometheusHttpListener(options => + { + options.UriPrefixes = new string[] { address }; + }) .Build(); break; @@ -165,11 +170,14 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri var content = await response.Content.ReadAsStringAsync(); var expected = requestOpenMetrics - ? "# TYPE counter_double_total counter\n" - + "counter_double_total{key1='value1',key2='value2'} 101.17 \\d+\\.\\d{3}\n" + ? "# TYPE otel_scope_info info\n" + + "# HELP otel_scope_info Scope metadata\n" + + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + + "# TYPE counter_double_total counter\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + "counter_double_total{key1='value1',key2='value2'} 101.17 \\d+\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; Assert.Matches(("^" + expected + "$").Replace('\'', '"'), content); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 4c221e0a55..55c4412b27 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -31,7 +31,7 @@ public void GaugeZeroDimension() Assert.Matches( ("^" + "# TYPE test_gauge gauge\n" - + "test_gauge 123 \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -57,7 +57,7 @@ public void GaugeZeroDimensionWithDescription() ("^" + "# TYPE test_gauge gauge\n" + "# HELP test_gauge Hello, world!\n" - + "test_gauge 123 \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -83,7 +83,7 @@ public void GaugeZeroDimensionWithUnit() ("^" + "# TYPE test_gauge_seconds gauge\n" + "# UNIT test_gauge_seconds seconds\n" - + "test_gauge_seconds 123 \\d+\n" + + $"test_gauge_seconds{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -110,7 +110,7 @@ public void GaugeZeroDimensionWithDescriptionAndUnit() + "# TYPE test_gauge_seconds gauge\n" + "# UNIT test_gauge_seconds seconds\n" + "# HELP test_gauge_seconds Hello, world!\n" - + "test_gauge_seconds 123 \\d+\n" + + $"test_gauge_seconds{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -137,7 +137,7 @@ public void GaugeOneDimension() Assert.Matches( ("^" + "# TYPE test_gauge gauge\n" - + "test_gauge{tagKey='tagValue'} 123 \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}',tagKey='tagValue'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -164,7 +164,7 @@ public void GaugeBoolDimension() Assert.Matches( ("^" + "# TYPE test_gauge gauge\n" - + "test_gauge{tagKey='true'} 123 \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}',tagKey='true'}} 123 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -194,9 +194,9 @@ public void GaugeDoubleSubnormal() Assert.Matches( ("^" + "# TYPE test_gauge gauge\n" - + "test_gauge{x='1',y='2'} -Inf \\d+\n" - + "test_gauge{x='3',y='4'} \\+Inf \\d+\n" - + "test_gauge{x='5',y='6'} Nan \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2'}} -Inf \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='3',y='4'}} \\+Inf \\d+\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='5',y='6'}} Nan \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -223,7 +223,7 @@ public void SumDoubleInfinities() Assert.Matches( ("^" + "# TYPE test_counter_total counter\n" - + "test_counter_total \\+Inf \\d+\n" + + $"test_counter_total{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} \\+Inf \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -250,7 +250,7 @@ public void SumNonMonotonicDouble() Assert.Matches( ("^" + "# TYPE test_updown_counter gauge\n" - + "test_updown_counter -1 \\d+\n" + + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -277,24 +277,24 @@ public void HistogramZeroDimension() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 2 \\d+\n" - + "test_histogram_bucket{le='250'} 2 \\d+\n" - + "test_histogram_bucket{le='500'} 2 \\d+\n" - + "test_histogram_bucket{le='750'} 2 \\d+\n" - + "test_histogram_bucket{le='1000'} 2 \\d+\n" - + "test_histogram_bucket{le='2500'} 2 \\d+\n" - + "test_histogram_bucket{le='5000'} 2 \\d+\n" - + "test_histogram_bucket{le='7500'} 2 \\d+\n" - + "test_histogram_bucket{le='10000'} 2 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum 118 \\d+\n" - + "test_histogram_count 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 2 \\d+\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 118 \\d+\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 2 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -321,24 +321,24 @@ public void HistogramOneDimension() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{x='1',le='0'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='5'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='10'} 0 \\d+\n" - + "test_histogram_bucket{x='1',le='25'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='50'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='75'} 1 \\d+\n" - + "test_histogram_bucket{x='1',le='100'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='250'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='750'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='1000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='2500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='5000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='7500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='10000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum{x='1'} 118 \\d+\n" - + "test_histogram_count{x='1'} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2 \\d+\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118 \\d+\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -365,24 +365,24 @@ public void HistogramTwoDimensions() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{x='1',y='2',le='0'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='5'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='10'} 0 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='25'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='50'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='75'} 1 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='100'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='250'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='750'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='1000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='2500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='5000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='7500'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='10000'} 2 \\d+\n" - + "test_histogram_bucket{x='1',y='2',le='\\+Inf'} 2 \\d+\n" - + "test_histogram_sum{x='1',y='2'} 118 \\d+\n" - + "test_histogram_count{x='1',y='2'} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='0'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='5'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='10'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='25'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='50'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='75'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='100'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='250'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='750'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='1000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='2500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='5000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='7500'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='10000'}} 2 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2',le='\\+Inf'}} 2 \\d+\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2'}} 118 \\d+\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',y='2'}} 2 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -410,24 +410,24 @@ public void HistogramInfinities() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 1 \\d+\n" - + "test_histogram_bucket{le='250'} 1 \\d+\n" - + "test_histogram_bucket{le='500'} 1 \\d+\n" - + "test_histogram_bucket{le='750'} 1 \\d+\n" - + "test_histogram_bucket{le='1000'} 1 \\d+\n" - + "test_histogram_bucket{le='2500'} 1 \\d+\n" - + "test_histogram_bucket{le='5000'} 1 \\d+\n" - + "test_histogram_bucket{le='7500'} 1 \\d+\n" - + "test_histogram_bucket{le='10000'} 1 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" - + "test_histogram_sum \\+Inf \\d+\n" - + "test_histogram_count 3 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 3 \\d+\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} \\+Inf \\d+\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 3 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -455,24 +455,24 @@ public void HistogramNaN() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{le='0'} 0 \\d+\n" - + "test_histogram_bucket{le='5'} 0 \\d+\n" - + "test_histogram_bucket{le='10'} 0 \\d+\n" - + "test_histogram_bucket{le='25'} 1 \\d+\n" - + "test_histogram_bucket{le='50'} 1 \\d+\n" - + "test_histogram_bucket{le='75'} 1 \\d+\n" - + "test_histogram_bucket{le='100'} 1 \\d+\n" - + "test_histogram_bucket{le='250'} 1 \\d+\n" - + "test_histogram_bucket{le='500'} 1 \\d+\n" - + "test_histogram_bucket{le='750'} 1 \\d+\n" - + "test_histogram_bucket{le='1000'} 1 \\d+\n" - + "test_histogram_bucket{le='2500'} 1 \\d+\n" - + "test_histogram_bucket{le='5000'} 1 \\d+\n" - + "test_histogram_bucket{le='7500'} 1 \\d+\n" - + "test_histogram_bucket{le='10000'} 1 \\d+\n" - + "test_histogram_bucket{le='\\+Inf'} 3 \\d+\n" - + "test_histogram_sum Nan \\d+\n" - + "test_histogram_count 3 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 1 \\d+\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 3 \\d+\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} Nan \\d+\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 3 \\d+\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } @@ -521,13 +521,13 @@ public void SumWithOpenMetricsFormat() Assert.Matches( ("^" + "# TYPE test_updown_counter gauge\n" - + "test_updown_counter -1 \\d+\\.\\d{3}\n" + + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 \\d+\\.\\d{{3}}\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } [Fact] - public void HistogramOneDimensionWithScopeInfo() + public void HistogramOneDimensionWithOpenMetricsFormat() { var buffer = new byte[85000]; var metrics = new List(); @@ -548,24 +548,107 @@ public void HistogramOneDimensionWithScopeInfo() Assert.Matches( ("^" + "# TYPE test_histogram histogram\n" - + "test_histogram_bucket{x='1',le='0'} 0 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='5'} 0 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='10'} 0 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='25'} 1 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='50'} 1 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='75'} 1 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='100'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='250'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='500'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='750'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='1000'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='2500'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='5000'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='7500'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='10000'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_bucket{x='1',le='\\+Inf'} 2 \\d+\\.\\d{3}\n" - + "test_histogram_sum{x='1'} 118 \\d+\\.\\d{3}\n" - + "test_histogram_count{x='1'} 2 \\d+\\.\\d{3}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118 \\d+\\.\\d{{3}}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2 \\d+\\.\\d{{3}}\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void ScopeInfo() + { + var buffer = new byte[85000]; + + var cursor = PrometheusSerializer.WriteScopeInfo(buffer, 0, "test_meter"); + + Assert.Matches( + ("^" + + "# TYPE otel_scope_info info\n" + + "# HELP otel_scope_info Scope metadata\n" + + "otel_scope_info{otel_scope_name='test_meter'} 1\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void SumWithScopeVersion() + { + var buffer = new byte[85000]; + var metrics = new List(); + using var meter = new Meter(Utils.GetCurrentMethodName(), "1.0.0"); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + var counter = meter.CreateUpDownCounter("test_updown_counter"); + counter.Add(10); + counter.Add(-11); + provider.ForceFlush(); + var cursor = WriteMetric(buffer, 0, metrics[0], true); + Assert.Matches( + ("^" + + "# TYPE test_updown_counter gauge\n" + + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0'}} -1 \\d+\\.\\d{{3}}\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + + [Fact] + public void HistogramOneDimensionWithScopeVersion() + { + var buffer = new byte[85000]; + var metrics = new List(); + + using var meter = new Meter(Utils.GetCurrentMethodName(), "1.0.0"); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var histogram = meter.CreateHistogram("test_histogram"); + histogram.Record(18, new KeyValuePair("x", "1")); + histogram.Record(100, new KeyValuePair("x", "1")); + + provider.ForceFlush(); + + var cursor = WriteMetric(buffer, 0, metrics[0], true); + Assert.Matches( + ("^" + + "# TYPE test_histogram histogram\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='0'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='5'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='10'}} 0 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='25'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='50'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='75'}} 1 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='100'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='250'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='750'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='1000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='2500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='5000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='7500'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='10000'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1',le='\\+Inf'}} 2 \\d+\\.\\d{{3}}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1'}} 118 \\d+\\.\\d{{3}}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',otel_scope_version='1.0.0',x='1'}} 2 \\d+\\.\\d{{3}}\n" + "$").Replace('\'', '"'), Encoding.UTF8.GetString(buffer, 0, cursor)); } diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 9888ab9a17..2d3f6d2c21 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -947,6 +947,7 @@ public async Task DiagnosticSourceExceptionCallBackIsNotReceivedForExceptionsHan int numberOfUnSubscribedEvents = 0; int numberOfSubscribedEvents = 0; int numberOfExceptionCallbacks = 0; + bool exceptionHandled = false; // configure SDK this.tracerProvider = Sdk.CreateTracerProviderBuilder() @@ -991,18 +992,18 @@ public async Task DiagnosticSourceExceptionCallBackIsNotReceivedForExceptionsHan }) .Build(); + TestMiddleware.Create(builder => builder + .UseExceptionHandler(handler => + handler.Run(async (ctx) => + { + exceptionHandled = true; + await ctx.Response.WriteAsync("handled"); + }))); + using (var client = this.factory .WithWebHostBuilder(builder => { builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - builder.Configure(app => app - .UseExceptionHandler(handler => - { - handler.Run(async (ctx) => - { - await ctx.Response.WriteAsync("handled"); - }); - })); }) .CreateClient()) { @@ -1020,6 +1021,7 @@ public async Task DiagnosticSourceExceptionCallBackIsNotReceivedForExceptionsHan Assert.Equal(0, numberOfExceptionCallbacks); Assert.Equal(0, numberOfUnSubscribedEvents); Assert.Equal(2, numberOfSubscribedEvents); + Assert.True(exceptionHandled); } public void Dispose() diff --git a/test/OpenTelemetry.Instrumentation.Grpc.Tests/GrpcTests.server.cs b/test/OpenTelemetry.Instrumentation.Grpc.Tests/GrpcTests.server.cs index 6b16c081d1..9ef4eaf9e7 100644 --- a/test/OpenTelemetry.Instrumentation.Grpc.Tests/GrpcTests.server.cs +++ b/test/OpenTelemetry.Instrumentation.Grpc.Tests/GrpcTests.server.cs @@ -3,8 +3,19 @@ #if NET6_0_OR_GREATER using System.Diagnostics; +using System.Net; +using Greet; +using Grpc.Core; +using Grpc.Net.Client; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Context.Propagation; using OpenTelemetry.Instrumentation.Grpc.Services.Tests; +using OpenTelemetry.Instrumentation.GrpcNetClient; +using OpenTelemetry.Trace; using Xunit; +using static OpenTelemetry.Internal.HttpSemanticConventionHelper; +using Status = OpenTelemetry.Trace.Status; namespace OpenTelemetry.Instrumentation.Grpc.Tests; @@ -21,34 +32,26 @@ public GrpcTests() this.server = new GrpcServer(); } - /* [Theory] [InlineData(null)] - [InlineData(true)] - [InlineData(false)] - public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(bool? enableGrpcAspNetCoreSupport) + [InlineData("true")] + [InlineData("false")] + [InlineData("True")] + [InlineData("False")] + public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(string enableGrpcAspNetCoreSupport) { var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) + .AddInMemoryCollection(new Dictionary + { + [SemanticConventionOptInKeyName] = "http", + ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION"] = enableGrpcAspNetCoreSupport, + }) .Build(); var exportedItems = new List(); - var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)); - - if (enableGrpcAspNetCoreSupport.HasValue) - { - tracerProviderBuilder.AddAspNetCoreInstrumentation(options => - { - options.EnableGrpcAspNetCoreSupport = enableGrpcAspNetCoreSupport.Value; - }); - } - else - { - tracerProviderBuilder.AddAspNetCoreInstrumentation(); - } - - using var tracerProvider = tracerProviderBuilder + using var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .AddAspNetCoreInstrumentation() .AddInMemoryExporter(exportedItems) .Build(); @@ -66,7 +69,7 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(bool? enableGrpcA Assert.Equal(ActivityKind.Server, activity.Kind); - if (!enableGrpcAspNetCoreSupport.HasValue || enableGrpcAspNetCoreSupport.Value) + if (enableGrpcAspNetCoreSupport != null && enableGrpcAspNetCoreSupport.Equals("true", StringComparison.OrdinalIgnoreCase)) { Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystem)); Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); @@ -99,30 +102,28 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(bool? enableGrpcA [Theory(Skip = "Skipping for .NET 6 and higher due to bug #3023")] #endif [InlineData(null)] - [InlineData(true)] - [InlineData(false)] - public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewActivity(bool? enableGrpcAspNetCoreSupport) + [InlineData("true")] + [InlineData("false")] + [InlineData("True")] + [InlineData("False")] + public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewActivity(string enableGrpcAspNetCoreSupport) { try { // B3Propagator along with the headers passed to the client.SayHello ensure that the instrumentation creates a sibling activity Sdk.SetDefaultTextMapPropagator(new Extensions.Propagators.B3Propagator()); var exportedItems = new List(); - var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder(); - - if (enableGrpcAspNetCoreSupport.HasValue) - { - tracerProviderBuilder.AddAspNetCoreInstrumentation(options => - { - options.EnableGrpcAspNetCoreSupport = enableGrpcAspNetCoreSupport.Value; - }); - } - else - { - tracerProviderBuilder.AddAspNetCoreInstrumentation(); - } - - using var tracerProvider = tracerProviderBuilder + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [SemanticConventionOptInKeyName] = "http", + ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION"] = enableGrpcAspNetCoreSupport, + }) + .Build(); + + using var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .AddAspNetCoreInstrumentation() .AddInMemoryExporter(exportedItems) .Build(); @@ -146,7 +147,7 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewAc Assert.Equal(ActivityKind.Server, activity.Kind); - if (!enableGrpcAspNetCoreSupport.HasValue || enableGrpcAspNetCoreSupport.Value) + if (enableGrpcAspNetCoreSupport != null && enableGrpcAspNetCoreSupport.Equals("true", StringComparison.OrdinalIgnoreCase)) { Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystem)); Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); @@ -183,7 +184,7 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewAc })); } } - */ + public void Dispose() { this.server.Dispose(); diff --git a/test/OpenTelemetry.Tests/Logs/LogRecordStateProcessorTests.cs b/test/OpenTelemetry.Tests/Logs/LogRecordStateProcessorTests.cs new file mode 100644 index 0000000000..24acd8869a --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/LogRecordStateProcessorTests.cs @@ -0,0 +1,266 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace OpenTelemetry.Logs.Tests; + +public class LogRecordStateProcessorTests +{ + [Theory] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(false, false)] + public void LogProcessorSetStateTest(bool includeAttributes, bool parseStateValues) + { + List exportedItems = new(); + + using (var loggerFactory = CreateLoggerFactory(includeAttributes, parseStateValues, exportedItems, OnEnd)) + { + var logger = loggerFactory.CreateLogger("TestLogger"); + + logger.LogInformation("Hello world {data}", 1234); + } + + Assert.Single(exportedItems); + + AssertStateAndAttributes( + exportedItems[0], + attributesExpectedCount: !includeAttributes ? 0 : parseStateValues ? 1 : 3, + stateExpectedCount: !includeAttributes || parseStateValues ? 1 : 3, + out var state, + out var attributes); + + void OnEnd(LogRecord logRecord) + { + AssertStateAndAttributes( + logRecord, + attributesExpectedCount: includeAttributes ? 2 : 0, + stateExpectedCount: !includeAttributes || parseStateValues ? 0 : 2, + out var state, + out var attributes); + + logRecord.State = new List>(state) + { + new("enrichedData", "OTel"), + }; + } + } + + [Theory] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(false, false)] + public void LogProcessorSetStateToUnsupportedTypeTest(bool includeAttributes, bool parseStateValues) + { + List exportedItems = new(); + + using (var loggerFactory = CreateLoggerFactory(includeAttributes, parseStateValues, exportedItems, OnEnd)) + { + var logger = loggerFactory.CreateLogger("TestLogger"); + + logger.LogInformation("Hello world {data}", 1234); + } + + Assert.Single(exportedItems); + + AssertStateAndAttributes( + exportedItems[0], + attributesExpectedCount: 0, + stateExpectedCount: 0, + out var state, + out var attributes); + + Assert.True(exportedItems[0].State is CustomState); + + void OnEnd(LogRecord logRecord) + { + AssertStateAndAttributes( + logRecord, + attributesExpectedCount: includeAttributes ? 2 : 0, + stateExpectedCount: !includeAttributes || parseStateValues ? 0 : 2, + out var state, + out var attributes); + + logRecord.State = new CustomState("OTel"); + } + } + + [Theory] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(false, false)] + public void LogProcessorSetAttributesTest(bool includeAttributes, bool parseStateValues) + { + List exportedItems = new(); + + using (var loggerFactory = CreateLoggerFactory(includeAttributes, parseStateValues, exportedItems, OnEnd)) + { + var logger = loggerFactory.CreateLogger("TestLogger"); + + logger.LogInformation("Hello world {data}", 1234); + } + + Assert.Single(exportedItems); + + AssertStateAndAttributes( + exportedItems[0], + attributesExpectedCount: !includeAttributes ? 1 : 3, + stateExpectedCount: !includeAttributes || parseStateValues ? 0 : 3, + out var state, + out var attributes); + + void OnEnd(LogRecord logRecord) + { + AssertStateAndAttributes( + logRecord, + attributesExpectedCount: includeAttributes ? 2 : 0, + stateExpectedCount: !includeAttributes || parseStateValues ? 0 : 2, + out var state, + out var attributes); + + logRecord.Attributes = new List>(attributes) + { + new("enrichedData", "OTel"), + }; + } + } + + [Theory] + [InlineData(true, false, 0)] + [InlineData(false, true, 0)] + [InlineData(true, true, 0)] + [InlineData(false, false, 0)] + [InlineData(true, false, 1)] + [InlineData(false, true, 1)] + [InlineData(true, true, 1)] + [InlineData(false, false, 1)] + public void LogProcessorSetAttributesAndStateMixedTest(bool includeAttributes, bool parseStateValues, int order) + { + List exportedItems = new(); + + using (var loggerFactory = CreateLoggerFactory(includeAttributes, parseStateValues, exportedItems, OnEnd)) + { + var logger = loggerFactory.CreateLogger("TestLogger"); + + logger.LogInformation("Hello world {data}", 1234); + } + + Assert.Single(exportedItems); + + AssertStateAndAttributes( + exportedItems[0], + attributesExpectedCount: !includeAttributes ? 1 : 3, + stateExpectedCount: !includeAttributes ? 1 : 3, + out var state, + out var attributes); + + void OnEnd(LogRecord logRecord) + { + AssertStateAndAttributes( + logRecord, + attributesExpectedCount: includeAttributes ? 2 : 0, + stateExpectedCount: !includeAttributes || parseStateValues ? 0 : 2, + out var state, + out var attributes); + + if (order == 0) + { + logRecord.State = logRecord.Attributes = new List>(attributes) + { + new("enrichedData", "OTel"), + }; + } + else + { + var newState = new List>(attributes) + { + new("enrichedData", "OTel"), + }; + + logRecord.State = newState; + logRecord.Attributes = newState; + } + } + } + + private static ILoggerFactory CreateLoggerFactory( + bool includeAttributes, + bool parseStateValues, + List exportedItems, + Action onEndAction) + { + return LoggerFactory.Create(logging => logging + .AddOpenTelemetry(options => + { + options.IncludeAttributes = includeAttributes; + options.ParseStateValues = parseStateValues; + + options + .AddProcessor(new LogRecordStateProcessor(onEndAction)) + .AddInMemoryExporter(exportedItems); + })); + } + + private static void AssertStateAndAttributes( + LogRecord logRecord, + int attributesExpectedCount, + int stateExpectedCount, + [NotNull] out IReadOnlyList>? state, + [NotNull] out IReadOnlyList>? attributes) + { + state = logRecord.State as IReadOnlyList>; + attributes = logRecord.Attributes; + + if (stateExpectedCount > 0) + { + Assert.NotNull(state); + Assert.Equal(stateExpectedCount, state.Count); + } + else + { + Assert.Null(state); + state = Array.Empty>(); + } + + if (attributesExpectedCount > 0) + { + Assert.NotNull(attributes); + Assert.Equal(attributesExpectedCount, attributes.Count); + } + else + { + Assert.Null(attributes); + attributes = Array.Empty>(); + } + } + + private sealed class LogRecordStateProcessor : BaseProcessor + { + private readonly Action onEndAction; + + public LogRecordStateProcessor(Action onEndAction) + { + this.onEndAction = onEndAction; + } + + public override void OnEnd(LogRecord data) + { + this.onEndAction(data); + + base.OnEnd(data); + } + } + + private sealed class CustomState(string enrichedData) + { + public string EnrichedData { get; } = enrichedData; + } +} diff --git a/test/TestApp.AspNetCore/Program.cs b/test/TestApp.AspNetCore/Program.cs index 49b801fb6c..06071eab4b 100644 --- a/test/TestApp.AspNetCore/Program.cs +++ b/test/TestApp.AspNetCore/Program.cs @@ -47,6 +47,8 @@ public static void Main(string[] args) app.UseMiddleware(); + app.AddTestMiddleware(); + app.Run(); } } diff --git a/test/TestApp.AspNetCore/TestMiddleware.cs b/test/TestApp.AspNetCore/TestMiddleware.cs new file mode 100644 index 0000000000..39acf58db3 --- /dev/null +++ b/test/TestApp.AspNetCore/TestMiddleware.cs @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +namespace TestApp.AspNetCore; + +public static class TestMiddleware +{ + private static readonly AsyncLocal?> Current = new(); + + public static IApplicationBuilder AddTestMiddleware(this IApplicationBuilder builder) + { + if (Current.Value is { } configure) + { + configure(builder); + } + + return builder; + } + + public static void Create(Action action) + { + Current.Value = action; + } +}