Capture HTTP Headers/gRPC Metadata as span attributes (#3444)
This commit is contained in:
parent
b288cb35e3
commit
86a32ceb4a
|
@ -5,6 +5,7 @@ bitness
|
|||
bytecode
|
||||
cmake
|
||||
Codespaces
|
||||
Contoso
|
||||
coreutils
|
||||
corhlpr
|
||||
Couchbase
|
||||
|
@ -28,8 +29,10 @@ MASSTRANSIT
|
|||
metricsexporter
|
||||
mkdir
|
||||
mktemp
|
||||
monocytogenes
|
||||
MSMQ
|
||||
myapp
|
||||
mycompanymyproductmylibrary
|
||||
MYSQLCONNECTOR
|
||||
MYSQLDATA
|
||||
NETRUNTIME
|
||||
|
|
|
@ -57,6 +57,9 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0 # fetching all, needed to correctly calculate version
|
||||
|
||||
- name: Set the GITHUB_RUNNER_SYSTEM environment variable
|
||||
run: echo "GITHUB_RUNNER_SYSTEM=${{ matrix.machine }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup ARM64 Environment Variables
|
||||
if: ${{ matrix.machine == 'actuated-arm64-4cpu-8gb' }}
|
||||
run: |
|
||||
|
|
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -9,6 +9,20 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|||
|
||||
### Added
|
||||
|
||||
- Support for capturing HTTP headers for following traces instrumentations:
|
||||
- ASP.NET, configurable by
|
||||
`OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS`
|
||||
and `OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS`,
|
||||
- ASP.NET Core, configurable by
|
||||
`OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS`
|
||||
and `OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS`,
|
||||
- HTTP Client, configurable by
|
||||
`OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS`
|
||||
and `OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS`.
|
||||
- Support for capturing gRPC metadata for Grpc.Net.Client traces instrumentation.
|
||||
It can by configured by
|
||||
`OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_REQUEST_METADATA`
|
||||
and `OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_RESPONSE_METADATA`.
|
||||
- Support for [Oracle.ManagedDataAccess.Core](https://www.nuget.org/packages/Oracle.ManagedDataAccess.Core)
|
||||
and [Oracle.ManagedDataAccess](https://www.nuget.org/packages/Oracle.ManagedDataAccess)
|
||||
traces instrumentation from 23.4.0 together with support for
|
||||
|
@ -23,6 +37,17 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|||
for details.
|
||||
- Do not create consumer spans related to `PartitionEOF` events
|
||||
for `Confluent.Kafka` client instrumentation.
|
||||
- Following properties can be set before calling plugins:
|
||||
- `OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest`,
|
||||
- `OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse`,
|
||||
- `OpenTelemetry.Instrumentation.AspNet.AspNetTraceInstrumentationOptions.EnrichWithHttpRequest`,
|
||||
- `OpenTelemetry.Instrumentation.AspNet.AspNetTraceInstrumentationOptions.EnrichWithHttpResponse`,
|
||||
- `OpenTelemetry.Instrumentation.GrpcNetClient.GrpcClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage`,
|
||||
- `OpenTelemetry.Instrumentation.GrpcNetClient.GrpcClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage`,
|
||||
- `OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpRequestMessage`,
|
||||
- `OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest`,
|
||||
- `OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpResponseMessage`,
|
||||
- `OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebResponse`.
|
||||
|
||||
#### Dependency updates
|
||||
|
||||
|
|
|
@ -203,15 +203,23 @@ the `ASPNETCORE_HOSTINGSTARTUPASSEMBLIES` environment variable to
|
|||
|
||||
### Instrumentation options
|
||||
|
||||
| Environment variable | Description | Default value | Status |
|
||||
|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `OTEL_DOTNET_AUTO_ENTITYFRAMEWORKCORE_SET_DBSTATEMENT_FOR_TEXT` | Whether the Entity Framework Core instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_GRAPHQL_SET_DOCUMENT` | Whether the GraphQL instrumentation can pass raw queries through the `graphql.document` attribute. Queries might contain sensitive information. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_ORACLEMDA_SET_DBSTATEMENT_FOR_TEXT` | Whether the Oracle Client instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_SQLCLIENT_SET_DBSTATEMENT_FOR_TEXT` | Whether the SQL Client instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. **Not supported on .NET Framework for System.Data.SqlClient.** | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` | Whether the ASP.NET Core instrumentation turns off redaction of the `url.query` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION` | Whether the HTTP client instrumentation turns off redaction of the `url.full` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_ASPNET_DISABLE_URL_QUERY_REDACTION` | Whether the ASP.NET instrumentation turns off redaction of the `url.query` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| Environment variable | Description | Default value | Status |
|
||||
|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `OTEL_DOTNET_AUTO_ENTITYFRAMEWORKCORE_SET_DBSTATEMENT_FOR_TEXT` | Whether the Entity Framework Core instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_GRAPHQL_SET_DOCUMENT` | Whether the GraphQL instrumentation can pass raw queries through the `graphql.document` attribute. Queries might contain sensitive information. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_ORACLEMDA_SET_DBSTATEMENT_FOR_TEXT` | Whether the Oracle Client instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_SQLCLIENT_SET_DBSTATEMENT_FOR_TEXT` | Whether the SQL Client instrumentation can pass SQL statements through the `db.statement` attribute. Queries might contain sensitive information. If set to `false`, `db.statement` is recorded only for executing stored procedures. **Not supported on .NET Framework for System.Data.SqlClient.** | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS` | A comma-separated list of HTTP header names. ASP.NET instrumentations will capture HTTP request header values for all configured header names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS` | A comma-separated list of HTTP header names. ASP.NET instrumentations will capture HTTP response header values for all configured header names. **Not supported on IIS Classic mode.** | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS` | A comma-separated list of HTTP header names. ASP.NET Core instrumentations will capture HTTP request header values for all configured header names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS` | A comma-separated list of HTTP header names. ASP.NET Core instrumentations will capture HTTP response header values for all configured header names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_REQUEST_METADATA` | A comma-separated list of gRPC metadata names. Grpc.Net.Client instrumentations will capture gRPC request metadata values for all configured metadata names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_RESPONSE_METADATA` | A comma-separated list of gRPC metadata names. Grpc.Net.Client instrumentations will capture gRPC response metadata values for all configured metadata names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS` | A comma-separated list of HTTP header names. HTTP Client instrumentations will capture HTTP request header values for all configured header names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS` | A comma-separated list of HTTP header names. HTTP Client instrumentations will capture HTTP response header values for all configured header names. | | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` | Whether the ASP.NET Core instrumentation turns off redaction of the `url.query` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION` | Whether the HTTP client instrumentation turns off redaction of the `url.full` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `OTEL_DOTNET_EXPERIMENTAL_ASPNET_DISABLE_URL_QUERY_REDACTION` | Whether the ASP.NET instrumentation turns off redaction of the `url.query` attribute value. | `false` | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
|
||||
## Propagators
|
||||
|
||||
|
|
|
@ -102,6 +102,12 @@ public class MyPlugin
|
|||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Automatic Instrumentation can configure particular properties before calling
|
||||
> `Configure{Signal}Methods`. It is plugin responsibility to not override this behavior.
|
||||
> Example: `OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions.EnrichWithHttpWebRequest`
|
||||
> is conditionally set by this project.
|
||||
|
||||
## Supported Options
|
||||
|
||||
### Tracing
|
||||
|
|
|
@ -25,4 +25,16 @@ internal static class ConfigurationExtensions
|
|||
|
||||
return enabledConfigurations;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> ParseList(this Configuration source, string key, char valueSeparator)
|
||||
{
|
||||
var values = source.GetString(key);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(values))
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
return values!.Split(new[] { valueSeparator }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,11 +108,60 @@ internal partial class ConfigurationKeys
|
|||
/// </summary>
|
||||
public static class InstrumentationOptions
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Configuration key for ASP.NET instrumentation to enable capturing HTTP request headers as span tags.
|
||||
/// </summary>
|
||||
public const string AspNetInstrumentationCaptureRequestHeaders = "OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for ASP.NET instrumentation to enable capturing HTTP response headers as span tags.
|
||||
/// </summary>
|
||||
public const string AspNetInstrumentationCaptureResponseHeaders = "OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS";
|
||||
#endif
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Configuration key for ASP.NET Core instrumentation to enable capturing HTTP request headers as span tags.
|
||||
/// </summary>
|
||||
public const string AspNetCoreInstrumentationCaptureRequestHeaders = "OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for ASP.NET Core instrumentation to enable capturing HTTP response headers as span tags.
|
||||
/// </summary>
|
||||
public const string AspNetCoreInstrumentationCaptureResponseHeaders = "OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for Entity Framework Core instrumentation to enable passing text query as a db.statement attribute.
|
||||
/// </summary>
|
||||
public const string EntityFrameworkCoreSetDbStatementForText = "OTEL_DOTNET_AUTO_ENTITYFRAMEWORKCORE_SET_DBSTATEMENT_FOR_TEXT";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for GraphQL instrumentation to enable passing query as a document attribute.
|
||||
/// </summary>
|
||||
public const string GraphQLSetDocument = "OTEL_DOTNET_AUTO_GRAPHQL_SET_DOCUMENT";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for GrpcNetClient instrumentation to enable capturing request metadata as span tags.
|
||||
/// </summary>
|
||||
public const string GrpcNetClientInstrumentationCaptureRequestMetadata = "OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_REQUEST_METADATA";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for GrpcNetClient instrumentation to enable capturing response metadata as span tags.
|
||||
/// </summary>
|
||||
public const string GrpcNetClientInstrumentationCaptureResponseMetadata = "OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_RESPONSE_METADATA";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for HTTP instrumentation to enable capturing HTTP request headers as span tags.
|
||||
/// </summary>
|
||||
public const string HttpInstrumentationCaptureRequestHeaders = "OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for HTTP instrumentation to enable capturing HTTP response headers as span tags.
|
||||
/// </summary>
|
||||
public const string HttpInstrumentationCaptureResponseHeaders = "OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration key for Oracle Client instrumentation to enable passing text query as a db.statement attribute.
|
||||
/// </summary>
|
||||
|
@ -122,13 +171,6 @@ internal partial class ConfigurationKeys
|
|||
/// Configuration key for SQL Client instrumentation to enable passing text query as a db.statement attribute.
|
||||
/// </summary>
|
||||
public const string SqlClientSetDbStatementForText = "OTEL_DOTNET_AUTO_SQLCLIENT_SET_DBSTATEMENT_FOR_TEXT";
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Configuration key for Entity Framework Core instrumentation to enable passing text query as a db.statement attribute.
|
||||
/// </summary>
|
||||
public const string EntityFrameworkCoreSetDbStatementForText = "OTEL_DOTNET_AUTO_ENTITYFRAMEWORKCORE_SET_DBSTATEMENT_FOR_TEXT";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,30 +14,30 @@ internal static class DelayedInitialization
|
|||
{
|
||||
#if NETFRAMEWORK
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void AddAspNet(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public static void AddAspNet(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
new AspNetInitializer(lazyInstrumentationLoader, pluginManager);
|
||||
new AspNetInitializer(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void AddAspNetCore(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public static void AddAspNetCore(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
lazyInstrumentationLoader.Add(new AspNetCoreInitializer(pluginManager));
|
||||
lazyInstrumentationLoader.Add(new AspNetCoreInitializer(pluginManager, tracerSettings));
|
||||
}
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void AddHttpClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public static void AddHttpClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
new HttpClientInitializer(lazyInstrumentationLoader, pluginManager);
|
||||
new HttpClientInitializer(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void AddGrpcClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public static void AddGrpcClient(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
lazyInstrumentationLoader.Add(new GrpcClientInitializer(pluginManager));
|
||||
lazyInstrumentationLoader.Add(new GrpcClientInitializer(pluginManager, tracerSettings));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
|
|
|
@ -25,11 +25,11 @@ internal static class EnvironmentConfigurationTracerHelper
|
|||
_ = enabledInstrumentation switch
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
TracerInstrumentation.AspNet => Wrappers.AddAspNetInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
|
||||
TracerInstrumentation.AspNet => Wrappers.AddAspNetInstrumentation(builder, pluginManager, lazyInstrumentationLoader, settings),
|
||||
TracerInstrumentation.WcfService => AddWcfIfNeeded(builder, pluginManager, lazyInstrumentationLoader, ref wcfInstrumentationAdded),
|
||||
#endif
|
||||
TracerInstrumentation.GrpcNetClient => Wrappers.AddGrpcClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
|
||||
TracerInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
|
||||
TracerInstrumentation.GrpcNetClient => Wrappers.AddGrpcClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader, settings),
|
||||
TracerInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader, settings),
|
||||
TracerInstrumentation.Npgsql => builder.AddSource("Npgsql"),
|
||||
TracerInstrumentation.SqlClient => Wrappers.AddSqlClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader, settings),
|
||||
TracerInstrumentation.NServiceBus => builder.AddSource("NServiceBus.Core"),
|
||||
|
@ -42,7 +42,7 @@ internal static class EnvironmentConfigurationTracerHelper
|
|||
TracerInstrumentation.WcfClient => AddWcfIfNeeded(builder, pluginManager, lazyInstrumentationLoader, ref wcfInstrumentationAdded),
|
||||
TracerInstrumentation.OracleMda => Wrappers.AddOracleMdaInstrumentation(builder, lazyInstrumentationLoader, settings),
|
||||
#if NET6_0_OR_GREATER
|
||||
TracerInstrumentation.AspNetCore => Wrappers.AddAspNetCoreInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
|
||||
TracerInstrumentation.AspNetCore => Wrappers.AddAspNetCoreInstrumentation(builder, pluginManager, lazyInstrumentationLoader, settings),
|
||||
TracerInstrumentation.MassTransit => builder.AddSource("MassTransit"),
|
||||
TracerInstrumentation.MySqlData => builder.AddSource("connector-net"),
|
||||
TracerInstrumentation.StackExchangeRedis => builder.AddSource("OpenTelemetry.Instrumentation.StackExchangeRedis"),
|
||||
|
@ -122,9 +122,9 @@ internal static class EnvironmentConfigurationTracerHelper
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)
|
||||
public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader, TracerSettings tracerSettings)
|
||||
{
|
||||
DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
|
||||
#if NETFRAMEWORK
|
||||
builder.AddSource("OpenTelemetry.Instrumentation.Http.HttpWebRequest");
|
||||
|
@ -139,18 +139,18 @@ internal static class EnvironmentConfigurationTracerHelper
|
|||
|
||||
#if NETFRAMEWORK
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static TracerProviderBuilder AddAspNetInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)
|
||||
public static TracerProviderBuilder AddAspNetInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader, TracerSettings tracerSettings)
|
||||
{
|
||||
DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
return builder.AddSource(OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.AspNetSourceName);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static TracerProviderBuilder AddAspNetCoreInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)
|
||||
public static TracerProviderBuilder AddAspNetCoreInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader, TracerSettings tracerSettings)
|
||||
{
|
||||
DelayedInitialization.Traces.AddAspNetCore(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddAspNetCore(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
|
||||
if (Environment.Version.Major == 6)
|
||||
{
|
||||
|
@ -198,9 +198,9 @@ internal static class EnvironmentConfigurationTracerHelper
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static TracerProviderBuilder AddGrpcClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)
|
||||
public static TracerProviderBuilder AddGrpcClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader, TracerSettings tracerSettings)
|
||||
{
|
||||
DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
|
||||
builder.AddSource("OpenTelemetry.Instrumentation.GrpcNetClient");
|
||||
builder.AddLegacySource("Grpc.Net.Client.GrpcOut");
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
|
||||
/// <summary>
|
||||
|
@ -10,19 +12,79 @@ internal class InstrumentationOptions
|
|||
{
|
||||
internal InstrumentationOptions(Configuration configuration)
|
||||
{
|
||||
GraphQLSetDocument = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.GraphQLSetDocument) ?? false;
|
||||
OracleMdaSetDbStatementForText = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.OracleMdaSetDbStatementForText) ?? false;
|
||||
SqlClientSetDbStatementForText = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.SqlClientSetDbStatementForText) ?? false;
|
||||
#if NETFRAMEWORK
|
||||
AspNetInstrumentationCaptureRequestHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.AspNetInstrumentationCaptureRequestHeaders, AdditionalTag.CreateHttpRequestCache);
|
||||
AspNetInstrumentationCaptureResponseHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.AspNetInstrumentationCaptureResponseHeaders, AdditionalTag.CreateHttpResponseCache);
|
||||
#endif
|
||||
#if NET6_0_OR_GREATER
|
||||
AspNetCoreInstrumentationCaptureRequestHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.AspNetCoreInstrumentationCaptureRequestHeaders, AdditionalTag.CreateHttpRequestCache);
|
||||
AspNetCoreInstrumentationCaptureResponseHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.AspNetCoreInstrumentationCaptureResponseHeaders, AdditionalTag.CreateHttpResponseCache);
|
||||
EntityFrameworkCoreSetDbStatementForText = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.EntityFrameworkCoreSetDbStatementForText) ?? false;
|
||||
#endif
|
||||
|
||||
GraphQLSetDocument = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.GraphQLSetDocument) ?? false;
|
||||
GrpcNetClientInstrumentationCaptureRequestMetadata = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.GrpcNetClientInstrumentationCaptureRequestMetadata, AdditionalTag.CreateGrpcRequestCache);
|
||||
GrpcNetClientInstrumentationCaptureResponseMetadata = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.GrpcNetClientInstrumentationCaptureResponseMetadata, AdditionalTag.CreateGrpcResponseCache);
|
||||
HttpInstrumentationCaptureRequestHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders, AdditionalTag.CreateHttpRequestCache);
|
||||
HttpInstrumentationCaptureResponseHeaders = configuration.ParseHeaders(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, AdditionalTag.CreateHttpResponseCache);
|
||||
OracleMdaSetDbStatementForText = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.OracleMdaSetDbStatementForText) ?? false;
|
||||
SqlClientSetDbStatementForText = configuration.GetBool(ConfigurationKeys.Traces.InstrumentationOptions.SqlClientSetDbStatementForText) ?? false;
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP request headers to be captured as the span tags by ASP.NET instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> AspNetInstrumentationCaptureRequestHeaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP response headers to be captured as the span tags by ASP.NET instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> AspNetInstrumentationCaptureResponseHeaders { get; }
|
||||
#endif
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP request headers to be captured as the span tags by ASP.NET Core instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> AspNetCoreInstrumentationCaptureRequestHeaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP response headers to be captured as the span tags by ASP.NET Core instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> AspNetCoreInstrumentationCaptureResponseHeaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether text query in Entity Framework Core can be passed as a db.statement tag.
|
||||
/// </summary>
|
||||
public bool EntityFrameworkCoreSetDbStatementForText { get; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether GraphQL query can be passed as a Document tag.
|
||||
/// </summary>
|
||||
public bool GraphQLSetDocument { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of request metadata to be captured as the span tags by Grpc.Net.Client instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> GrpcNetClientInstrumentationCaptureRequestMetadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of response metadata to be captured as the span tags by Grpc.Net.Client instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> GrpcNetClientInstrumentationCaptureResponseMetadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP request headers to be captured as the span tags by HTTP instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> HttpInstrumentationCaptureRequestHeaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of HTTP response headers to be captured as the span tags by HTTP instrumentation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<AdditionalTag> HttpInstrumentationCaptureResponseHeaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether text query in Oracle Client can be passed as a db.statement tag.
|
||||
/// </summary>
|
||||
|
@ -32,11 +94,4 @@ internal class InstrumentationOptions
|
|||
/// Gets a value indicating whether text query in SQL Client can be passed as a db.statement tag.
|
||||
/// </summary>
|
||||
public bool SqlClientSetDbStatementForText { get; }
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether text query in Entity Framework Core can be passed as a db.statement tag.
|
||||
/// </summary>
|
||||
public bool EntityFrameworkCoreSetDbStatementForText { get; }
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -12,6 +12,18 @@ internal static class Constants
|
|||
public const string TelemetryDistroVersionAttributeName = "telemetry.distro.version";
|
||||
}
|
||||
|
||||
public static class GrpcSpanAttributes
|
||||
{
|
||||
public const string AttributeGrpcRequestMetadataPrefix = "rpc.grpc.request.metadata";
|
||||
public const string AttributeGrpcResponseMetadataPrefix = "rpc.grpc.response.metadata";
|
||||
}
|
||||
|
||||
public static class HttpSpanAttributes
|
||||
{
|
||||
public const string AttributeHttpRequestHeaderPrefix = "http.request.header";
|
||||
public const string AttributeHttpResponseHeaderPrefix = "http.response.header";
|
||||
}
|
||||
|
||||
public static class ConfigurationValues
|
||||
{
|
||||
public const string None = "none";
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http.Headers;
|
||||
#if NET6_0_OR_GREATER
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
|
||||
internal static class ActivityExtensions
|
||||
{
|
||||
public static void AddHeadersAsTags(this Activity activity, IReadOnlyList<AdditionalTag> additionalTags, NameValueCollection headers)
|
||||
{
|
||||
if (!activity.IsAllDataRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < additionalTags.Count; i++)
|
||||
{
|
||||
var additionalTag = additionalTags[i];
|
||||
var headerValues = headers.GetValues(additionalTag.Key);
|
||||
|
||||
if (headerValues == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (headerValues.Length == 1)
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValues[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
public static void AddHeadersAsTags(this Activity activity, IReadOnlyList<AdditionalTag> additionalTags, IHeaderDictionary headers)
|
||||
{
|
||||
if (!activity.IsAllDataRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < additionalTags.Count; i++)
|
||||
{
|
||||
var additionalTag = additionalTags[i];
|
||||
|
||||
if (!headers.TryGetValue(additionalTag.Key, out var headerValues))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (headerValues.Count == 1)
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValues[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void AddHeadersAsTags(this Activity activity, IReadOnlyList<AdditionalTag> additionalTags, HttpHeaders headers)
|
||||
{
|
||||
if (!activity.IsAllDataRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < additionalTags.Count; i++)
|
||||
{
|
||||
var additionalTag = additionalTags[i];
|
||||
|
||||
if (!headers.TryGetValues(additionalTag.Key, out var headerValues))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var headerValuesAsArray = headerValues.ToArray();
|
||||
|
||||
if (headerValuesAsArray.Length == 1)
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValuesAsArray[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.SetTag(additionalTag.TagName, headerValuesAsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
|
||||
internal class AdditionalTag
|
||||
{
|
||||
private AdditionalTag(string key, string spanTagPrefix)
|
||||
{
|
||||
Key = key;
|
||||
TagName = $"{spanTagPrefix}.{HeaderNormalizer.Normalize(key)}";
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public string TagName { get; }
|
||||
|
||||
public static AdditionalTag CreateGrpcRequestCache(string key)
|
||||
{
|
||||
return new AdditionalTag(key, Constants.GrpcSpanAttributes.AttributeGrpcRequestMetadataPrefix);
|
||||
}
|
||||
|
||||
public static AdditionalTag CreateGrpcResponseCache(string key)
|
||||
{
|
||||
return new AdditionalTag(key, Constants.GrpcSpanAttributes.AttributeGrpcResponseMetadataPrefix);
|
||||
}
|
||||
|
||||
public static AdditionalTag CreateHttpRequestCache(string key)
|
||||
{
|
||||
return new AdditionalTag(key, Constants.HttpSpanAttributes.AttributeHttpRequestHeaderPrefix);
|
||||
}
|
||||
|
||||
public static AdditionalTag CreateHttpResponseCache(string key)
|
||||
{
|
||||
return new AdditionalTag(key, Constants.HttpSpanAttributes.AttributeHttpResponseHeaderPrefix);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
|
||||
internal static class HeaderConfigurationExtensions
|
||||
{
|
||||
public static IReadOnlyList<AdditionalTag> ParseHeaders(this Configuration source, string key, Func<string, AdditionalTag> stringToHeaderCacheConverter)
|
||||
{
|
||||
var headers = source.ParseList(key, ',');
|
||||
|
||||
if (headers.Count == 0)
|
||||
{
|
||||
return Array.Empty<AdditionalTag>();
|
||||
}
|
||||
|
||||
return headers.Select(stringToHeaderCacheConverter).ToArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
|
||||
internal static class HeaderNormalizer
|
||||
{
|
||||
public static string Normalize(string httpHeaderName)
|
||||
{
|
||||
return httpHeaderName.ToLowerInvariant();
|
||||
}
|
||||
}
|
|
@ -321,17 +321,17 @@ internal static class Instrumentation
|
|||
{
|
||||
#if NETFRAMEWORK
|
||||
case TracerInstrumentation.AspNet:
|
||||
DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
break;
|
||||
case TracerInstrumentation.WcfService:
|
||||
AddWcfIfNeeded(lazyInstrumentationLoader, pluginManager, ref wcfInstrumentationAdded);
|
||||
break;
|
||||
#endif
|
||||
case TracerInstrumentation.HttpClient:
|
||||
DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddHttpClient(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
break;
|
||||
case TracerInstrumentation.GrpcNetClient:
|
||||
DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddGrpcClient(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
break;
|
||||
case TracerInstrumentation.SqlClient:
|
||||
DelayedInitialization.Traces.AddSqlClient(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
|
@ -344,7 +344,7 @@ internal static class Instrumentation
|
|||
break;
|
||||
#if NET6_0_OR_GREATER
|
||||
case TracerInstrumentation.AspNetCore:
|
||||
DelayedInitialization.Traces.AddAspNetCore(lazyInstrumentationLoader, pluginManager);
|
||||
DelayedInitialization.Traces.AddAspNetCore(lazyInstrumentationLoader, pluginManager, tracerSettings);
|
||||
break;
|
||||
case TracerInstrumentation.MySqlData:
|
||||
break;
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
using OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
using OpenTelemetry.AutoInstrumentation.Plugins;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
||||
|
@ -9,11 +13,13 @@ namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
|||
internal class AspNetCoreInitializer : InstrumentationInitializer
|
||||
{
|
||||
private readonly PluginManager _pluginManager;
|
||||
private readonly TracerSettings _tracerSettings;
|
||||
|
||||
public AspNetCoreInitializer(PluginManager pluginManager)
|
||||
public AspNetCoreInitializer(PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
: base("Microsoft.AspNetCore.Http")
|
||||
{
|
||||
_pluginManager = pluginManager;
|
||||
_tracerSettings = tracerSettings;
|
||||
}
|
||||
|
||||
public override void Initialize(ILifespanManager lifespanManager)
|
||||
|
@ -22,6 +28,17 @@ internal class AspNetCoreInitializer : InstrumentationInitializer
|
|||
var httpInListenerType = Type.GetType("OpenTelemetry.Instrumentation.AspNetCore.Implementation.HttpInListener, OpenTelemetry.Instrumentation.AspNetCore")!;
|
||||
|
||||
var options = new OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions();
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.AspNetCoreInstrumentationCaptureRequestHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpRequest = EnrichWithHttpRequest;
|
||||
}
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.AspNetCoreInstrumentationCaptureResponseHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpResponse = EnrichWithHttpResponse;
|
||||
}
|
||||
|
||||
_pluginManager.ConfigureTracesOptions(options);
|
||||
|
||||
var httpInListener = Activator.CreateInstance(httpInListenerType, args: options);
|
||||
|
@ -29,5 +46,15 @@ internal class AspNetCoreInitializer : InstrumentationInitializer
|
|||
|
||||
lifespanManager.Track(instrumentation);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpRequest(Activity activity, HttpRequest httpRequest)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.AspNetCoreInstrumentationCaptureRequestHeaders, httpRequest.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpResponse(Activity activity, HttpResponse httpResponse)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.AspNetCoreInstrumentationCaptureResponseHeaders, httpResponse.Headers);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#if NETFRAMEWORK
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Web;
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
using OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
using OpenTelemetry.AutoInstrumentation.Plugins;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
||||
|
@ -10,12 +14,14 @@ namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
|||
internal class AspNetInitializer
|
||||
{
|
||||
private readonly PluginManager _pluginManager;
|
||||
private readonly TracerSettings _tracerSettings;
|
||||
|
||||
private int _initialized;
|
||||
|
||||
public AspNetInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public AspNetInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
_pluginManager = pluginManager;
|
||||
_tracerSettings = tracerSettings;
|
||||
lazyInstrumentationLoader.Add(new AspNetMvcInitializer(InitializeOnFirstCall));
|
||||
lazyInstrumentationLoader.Add(new AspNetWebApiInitializer(InitializeOnFirstCall));
|
||||
}
|
||||
|
@ -31,11 +37,32 @@ internal class AspNetInitializer
|
|||
var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentation, OpenTelemetry.Instrumentation.AspNet");
|
||||
|
||||
var options = new OpenTelemetry.Instrumentation.AspNet.AspNetTraceInstrumentationOptions();
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.AspNetInstrumentationCaptureRequestHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpRequest = EnrichWithHttpRequest;
|
||||
}
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.AspNetInstrumentationCaptureResponseHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpResponse = EnrichWithHttpResponse;
|
||||
}
|
||||
|
||||
_pluginManager.ConfigureTracesOptions(options);
|
||||
|
||||
var instrumentation = Activator.CreateInstance(instrumentationType, args: options);
|
||||
|
||||
lifespanManager.Track(instrumentation);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpRequest(Activity activity, HttpRequest httpRequest)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.AspNetInstrumentationCaptureRequestHeaders, httpRequest.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpResponse(Activity activity, HttpResponse httpResponse)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.AspNetInstrumentationCaptureResponseHeaders, httpResponse.Headers);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
using OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
using OpenTelemetry.AutoInstrumentation.Plugins;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
||||
|
@ -9,11 +12,13 @@ namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
|||
internal class GrpcClientInitializer : InstrumentationInitializer
|
||||
{
|
||||
private readonly PluginManager _pluginManager;
|
||||
private readonly TracerSettings _tracerSettings;
|
||||
|
||||
public GrpcClientInitializer(PluginManager pluginManager)
|
||||
public GrpcClientInitializer(PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
: base("Grpc.Net.Client")
|
||||
{
|
||||
_pluginManager = pluginManager;
|
||||
_tracerSettings = tracerSettings;
|
||||
}
|
||||
|
||||
public override void Initialize(ILifespanManager lifespanManager)
|
||||
|
@ -25,10 +30,30 @@ internal class GrpcClientInitializer : InstrumentationInitializer
|
|||
SuppressDownstreamInstrumentation = !Instrumentation.TracerSettings.Value.EnabledInstrumentations.Contains(TracerInstrumentation.HttpClient)
|
||||
};
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureRequestMetadata.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpRequestMessage = EnrichWithHttpRequestMessage;
|
||||
}
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureResponseMetadata.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpResponseMessage = EnrichWithHttpResponseMessage;
|
||||
}
|
||||
|
||||
_pluginManager.ConfigureTracesOptions(options);
|
||||
|
||||
var instrumentation = Activator.CreateInstance(instrumentationType, options)!;
|
||||
|
||||
lifespanManager.Track(instrumentation);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpRequestMessage(Activity activity, HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureRequestMetadata, httpRequestMessage.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpResponseMessage(Activity activity, HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureResponseMetadata, httpResponseMessage.Headers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
using OpenTelemetry.AutoInstrumentation.HeadersCapture;
|
||||
using OpenTelemetry.AutoInstrumentation.Plugins;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
||||
|
@ -9,12 +16,14 @@ namespace OpenTelemetry.AutoInstrumentation.Loading.Initializers;
|
|||
internal class HttpClientInitializer
|
||||
{
|
||||
private readonly PluginManager _pluginManager;
|
||||
private readonly TracerSettings _tracerSettings;
|
||||
|
||||
private int _initialized;
|
||||
|
||||
public HttpClientInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
|
||||
public HttpClientInitializer(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
|
||||
{
|
||||
_pluginManager = pluginManager;
|
||||
_tracerSettings = tracerSettings;
|
||||
|
||||
lazyInstrumentationLoader.Add(new GenericInitializer("System.Net.Http", InitializeOnFirstCall));
|
||||
|
||||
|
@ -32,6 +41,19 @@ internal class HttpClientInitializer
|
|||
}
|
||||
|
||||
var options = new OpenTelemetry.Instrumentation.Http.HttpClientTraceInstrumentationOptions();
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpRequestMessage = EnrichWithHttpRequestMessage;
|
||||
options.EnrichWithHttpWebRequest = EnrichWithHttpWebRequest;
|
||||
}
|
||||
|
||||
if (_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders.Count != 0)
|
||||
{
|
||||
options.EnrichWithHttpResponseMessage = EnrichWithHttpResponseMessage;
|
||||
options.EnrichWithHttpWebResponse = EnrichWithHttpWebResponse;
|
||||
}
|
||||
|
||||
_pluginManager.ConfigureTracesOptions(options);
|
||||
|
||||
#if NETFRAMEWORK
|
||||
|
@ -45,4 +67,24 @@ internal class HttpClientInitializer
|
|||
lifespanManager.Track(instrumentation);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void EnrichWithHttpRequestMessage(Activity activity, HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders, httpRequestMessage.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpWebRequest(Activity activity, HttpWebRequest httpWebRequest)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders, httpWebRequest.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpResponseMessage(Activity activity, HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, httpResponseMessage.Headers);
|
||||
}
|
||||
|
||||
private void EnrichWithHttpWebResponse(Activity activity, HttpWebResponse httpWebResponse)
|
||||
{
|
||||
activity.AddHeadersAsTags(_tracerSettings.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, httpWebResponse.Headers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<PackageVersion Include="GraphQL.MicrosoftDI" Version="7.8.0" />
|
||||
<PackageVersion Include="GraphQL.Server.Transports.AspNetCore" Version="7.7.1" />
|
||||
<PackageVersion Include="GraphQL.Server.Ui.Playground" Version="7.7.1" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.63.0" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.63.0" />
|
||||
<PackageVersion Include="Grpc.Net.Client.Web" Version="2.63.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.51.0" />
|
||||
|
|
|
@ -51,6 +51,72 @@ public class AspNetTests
|
|||
collector.AssertExpectations();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "EndToEnd")]
|
||||
[Trait("Containers", "Windows")]
|
||||
[InlineData("Classic")]
|
||||
[InlineData("Integrated")]
|
||||
public async Task SubmitTracesCapturesHttpHeaders(string appPoolMode)
|
||||
{
|
||||
Assert.True(EnvironmentTools.IsWindowsAdministrator(), "This test requires Windows Administrator privileges.");
|
||||
|
||||
// Using "*" as host requires Administrator. This is needed to make the mock collector endpoint
|
||||
// accessible to the Windows docker container where the test application is executed by binding
|
||||
// the endpoint to all network interfaces. In order to do that it is necessary to open the port
|
||||
// on the firewall.
|
||||
using var collector = new MockSpansCollector(Output, host: "*");
|
||||
using var fwPort = FirewallHelper.OpenWinPort(collector.Port, Output);
|
||||
collector.Expect("OpenTelemetry.Instrumentation.AspNet.Telemetry", span => // Expect Mvc span
|
||||
{
|
||||
if (appPoolMode == "Classic")
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header2");
|
||||
}
|
||||
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header3")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header1" && x.Value.StringValue == "Test-Value4")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header3" && x.Value.StringValue == "Test-Value6")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header2");
|
||||
});
|
||||
|
||||
collector.Expect("OpenTelemetry.Instrumentation.AspNet.Telemetry", span => // Expect WebApi span
|
||||
{
|
||||
if (appPoolMode == "Classic")
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header2");
|
||||
}
|
||||
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header3")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header1" && x.Value.StringValue == "Test-Value1")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header3" && x.Value.StringValue == "Test-Value3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header2");
|
||||
});
|
||||
|
||||
var collectorUrl = $"http://{DockerNetworkHelper.IntegrationTestsGateway}:{collector.Port}";
|
||||
_environmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = collectorUrl;
|
||||
_environmentVariables["OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS"] = "Custom-Request-Test-Header2";
|
||||
_environmentVariables["OTEL_DOTNET_AUTO_TRACES_ASPNET_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS"] = "Custom-Response-Test-Header1,Custom-Response-Test-Header3";
|
||||
var webPort = TcpPortProvider.GetOpenPort();
|
||||
await using var container = await StartContainerAsync(webPort, appPoolMode);
|
||||
await CallTestApplicationEndpoint(webPort);
|
||||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "EndToEnd")]
|
||||
[Trait("Containers", "Windows")]
|
||||
|
@ -156,6 +222,10 @@ public class AspNetTests
|
|||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
client.DefaultRequestHeaders.Add("Custom-Request-Test-Header1", "Test-Value1");
|
||||
client.DefaultRequestHeaders.Add("Custom-Request-Test-Header2", "Test-Value2");
|
||||
client.DefaultRequestHeaders.Add("Custom-Request-Test-Header3", "Test-Value3");
|
||||
|
||||
var response = await client.GetAsync($"http://localhost:{webPort}");
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Output.WriteLine("MVC Response:");
|
||||
|
|
|
@ -30,4 +30,38 @@ public class GrpcNetClientTests : TestHelper
|
|||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "EndToEnd")]
|
||||
[MemberData(nameof(LibraryVersion.GrpcNetClient), MemberType = typeof(LibraryVersion))]
|
||||
public void SubmitTracesCapturesGrpcMetadata(string packageVersion)
|
||||
{
|
||||
using var collector = new MockSpansCollector(Output);
|
||||
SetExporter(collector);
|
||||
|
||||
collector.Expect("OpenTelemetry.Instrumentation.GrpcNetClient", span =>
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "rpc.grpc.request.metadata.custom-request-test-header1" && x.Value.StringValue == "Test-Value1")
|
||||
&& span.Attributes.Any(x => x.Key == "rpc.grpc.request.metadata.custom-request-test-header3" && x.Value.StringValue == "Test-Value3")
|
||||
&& span.Attributes.All(x => x.Key != "rpc.grpc.request.metadata.custom-request-test-header2")
|
||||
#if NETFRAMEWORK
|
||||
; // there is no .NET Framework server
|
||||
#else
|
||||
&& span.Attributes.Any(x => x.Key == "rpc.grpc.response.metadata.custom-response-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "rpc.grpc.response.metadata.custom-response-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "rpc.grpc.response.metadata.custom-response-test-header3");
|
||||
#endif
|
||||
});
|
||||
|
||||
// Grpc.Net.Client is using various version of http communication under the hood.
|
||||
// Enabling only GrpcNetClient instrumentation to have consistent set of spans.
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_ENABLED", "false");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_ENABLED", "true");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_REQUEST_METADATA", "Custom-Request-Test-Header1,Custom-Request-Test-Header3");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_CAPTURE_RESPONSE_METADATA", "Custom-Response-Test-Header2");
|
||||
|
||||
RunTestApplication(new TestSettings { PackageVersion = packageVersion });
|
||||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,5 +27,29 @@ public class HttpNetFrameworkTests : TestHelper
|
|||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubmitTracesCapturesHttpHeaders()
|
||||
{
|
||||
using var collector = new MockSpansCollector(Output);
|
||||
SetExporter(collector);
|
||||
|
||||
collector.Expect("OpenTelemetry.Instrumentation.Http.HttpWebRequest", span =>
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header1" && x.Value.StringValue == "Test-Value1")
|
||||
&& span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header3" && x.Value.StringValue == "Test-Value3")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header2")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header3");
|
||||
});
|
||||
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS", "Custom-Request-Test-Header1,Custom-Request-Test-Header3");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS", "Custom-Response-Test-Header2");
|
||||
|
||||
RunTestApplication();
|
||||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -36,6 +36,7 @@ public class HttpTests : TestHelper
|
|||
clientSpan = span;
|
||||
return true;
|
||||
});
|
||||
|
||||
Span? serverSpan = null;
|
||||
#if NET7_0_OR_GREATER
|
||||
collector.Expect("Microsoft.AspNetCore", span =>
|
||||
|
@ -46,6 +47,7 @@ public class HttpTests : TestHelper
|
|||
serverSpan = span;
|
||||
return true;
|
||||
});
|
||||
|
||||
Span? manualSpan = null;
|
||||
collector.Expect("TestApplication.Http", span =>
|
||||
{
|
||||
|
@ -67,6 +69,50 @@ public class HttpTests : TestHelper
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubmitTracesCapturesHttpHeaders()
|
||||
{
|
||||
using var collector = new MockSpansCollector(Output);
|
||||
SetExporter(collector);
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
collector.Expect("System.Net.Http", span =>
|
||||
#else
|
||||
collector.Expect("OpenTelemetry.Instrumentation.Http.HttpClient", span =>
|
||||
#endif
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header1" && x.Value.StringValue == "Test-Value1")
|
||||
&& span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header3" && x.Value.StringValue == "Test-Value3")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header2")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header3");
|
||||
});
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
collector.Expect("Microsoft.AspNetCore", span =>
|
||||
#else
|
||||
collector.Expect("OpenTelemetry.Instrumentation.AspNetCore", span =>
|
||||
#endif
|
||||
{
|
||||
return span.Attributes.Any(x => x.Key == "http.request.header.custom-request-test-header2" && x.Value.StringValue == "Test-Value2")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header1")
|
||||
&& span.Attributes.All(x => x.Key != "http.request.header.custom-request-test-header3")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header1" && x.Value.StringValue == "Test-Value1")
|
||||
&& span.Attributes.Any(x => x.Key == "http.response.header.custom-response-test-header3" && x.Value.StringValue == "Test-Value3")
|
||||
&& span.Attributes.All(x => x.Key != "http.response.header.custom-response-test-header2");
|
||||
});
|
||||
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS", "Custom-Request-Test-Header1,Custom-Request-Test-Header3");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTP_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS", "Custom-Response-Test-Header2");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_REQUEST_HEADERS", "Custom-Request-Test-Header2");
|
||||
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ASPNETCORE_INSTRUMENTATION_CAPTURE_RESPONSE_HEADERS", "Custom-Response-Test-Header1,Custom-Response-Test-Header3");
|
||||
|
||||
RunTestApplication();
|
||||
|
||||
collector.AssertExpectations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "EndToEnd")]
|
||||
public void SubmitMetrics()
|
||||
|
|
|
@ -105,7 +105,7 @@ public class ConfigurationTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseEmptyAsNull_CompositeConfigurationSource()
|
||||
public void ParseEnabledEnumList_ParseEmptyAsNull_CompositeConfigurationSource()
|
||||
{
|
||||
var mockSource = Substitute.For<IConfigurationSource>();
|
||||
mockSource.GetString(Arg.Is<string>(key => key == "TEST_NULL_VALUE")).Returns(_ => null);
|
||||
|
@ -118,4 +118,53 @@ public class ConfigurationTests
|
|||
compositeSource.GetString("TEST_EMPTY_VALUE").Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseList_ParseSingleElement()
|
||||
{
|
||||
var source = new Configuration(false, new NameValueConfigurationSource(false, new NameValueCollection
|
||||
{
|
||||
{ "TEST_LIST", "Value1" },
|
||||
}));
|
||||
|
||||
var list = source.ParseList("TEST_LIST", ',');
|
||||
|
||||
list.Should().Equal("Value1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseList_ParseMultipleList()
|
||||
{
|
||||
var source = new Configuration(false, new NameValueConfigurationSource(false, new NameValueCollection
|
||||
{
|
||||
{ "TEST_LIST", "Value1,Value2" },
|
||||
}));
|
||||
|
||||
var list = source.ParseList("TEST_LIST", ',');
|
||||
|
||||
list.Should().Equal("Value1", "Value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseList_ParseEmptyEntry()
|
||||
{
|
||||
var source = new Configuration(false, new NameValueConfigurationSource(false, new NameValueCollection
|
||||
{
|
||||
{ "TEST_LIST", "Value1,,Value2" },
|
||||
}));
|
||||
|
||||
var list = source.ParseList("TEST_LIST", ',');
|
||||
|
||||
list.Should().Equal("Value1", "Value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseList_ParseNullAsEmpty()
|
||||
{
|
||||
var source = new Configuration(false, new NameValueConfigurationSource(false, new NameValueCollection()));
|
||||
|
||||
var list = source.ParseList("TEST_LIST", ',');
|
||||
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,22 @@ public class SettingsTests : IDisposable
|
|||
settings.AdditionalLegacySources.Should().BeEmpty();
|
||||
|
||||
// Instrumentation options tests
|
||||
settings.InstrumentationOptions.GraphQLSetDocument.Should().BeFalse();
|
||||
settings.InstrumentationOptions.OracleMdaSetDbStatementForText.Should().BeFalse();
|
||||
settings.InstrumentationOptions.SqlClientSetDbStatementForText.Should().BeFalse();
|
||||
#if NETFRAMEWORK
|
||||
settings.InstrumentationOptions.AspNetInstrumentationCaptureRequestHeaders.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.AspNetInstrumentationCaptureResponseHeaders.Should().BeEmpty();
|
||||
#endif
|
||||
#if NET6_0_OR_GREATER
|
||||
settings.InstrumentationOptions.AspNetCoreInstrumentationCaptureRequestHeaders.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.AspNetCoreInstrumentationCaptureResponseHeaders.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.EntityFrameworkCoreSetDbStatementForText.Should().BeFalse();
|
||||
#endif
|
||||
settings.InstrumentationOptions.GraphQLSetDocument.Should().BeFalse();
|
||||
settings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureRequestMetadata.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.GrpcNetClientInstrumentationCaptureResponseMetadata.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders.Should().BeEmpty();
|
||||
settings.InstrumentationOptions.OracleMdaSetDbStatementForText.Should().BeFalse();
|
||||
settings.InstrumentationOptions.SqlClientSetDbStatementForText.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +379,6 @@ public class SettingsTests : IDisposable
|
|||
}
|
||||
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Exporter, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.GraphQLSetDocument, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.ExporterOtlpProtocol, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.FlushOnUnhandledException, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.ResourceDetectorEnabled, null);
|
||||
|
@ -381,5 +390,22 @@ public class SettingsTests : IDisposable
|
|||
}
|
||||
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Sdk.Propagators, null);
|
||||
|
||||
#if NETFRAMEWORK
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.AspNetInstrumentationCaptureRequestHeaders, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.AspNetInstrumentationCaptureResponseHeaders, null);
|
||||
#endif
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.AspNetCoreInstrumentationCaptureRequestHeaders, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.AspNetCoreInstrumentationCaptureResponseHeaders, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.GraphQLSetDocument, null);
|
||||
#endif
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.GrpcNetClientInstrumentationCaptureRequestMetadata, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.GrpcNetClientInstrumentationCaptureResponseMetadata, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureRequestHeaders, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.HttpInstrumentationCaptureResponseHeaders, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.OracleMdaSetDbStatementForText, null);
|
||||
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.InstrumentationOptions.SqlClientSetDbStatementForText, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using TestApplication.AspNet.NetFramework.Helpers;
|
||||
|
@ -32,6 +33,19 @@ public class HomeController : Controller
|
|||
ViewBag.TracerAssemblies = AssembliesHelper.GetLoadedTracesAssemblies();
|
||||
ViewBag.AllAssemblies = AssembliesHelper.GetLoadedAssemblies();
|
||||
|
||||
try
|
||||
{
|
||||
var headers = HttpContext.Response.Headers;
|
||||
|
||||
headers.Add("Custom-Response-Test-Header1", "Test-Value4");
|
||||
headers.Add("Custom-Response-Test-Header2", "Test-Value5");
|
||||
headers.Add("Custom-Response-Test-Header3", "Test-Value6");
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
// do nothing, it can be raised on classic mode
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Web.Http;
|
||||
|
||||
namespace TestApplication.AspNet.NetFramework.Controllers;
|
||||
|
@ -22,9 +24,13 @@ namespace TestApplication.AspNet.NetFramework.Controllers;
|
|||
public class ValuesController : ApiController
|
||||
{
|
||||
// GET api/values
|
||||
public IEnumerable<string> Get()
|
||||
public HttpResponseMessage Get()
|
||||
{
|
||||
return new string[] { "value1", "value2" };
|
||||
var response = Request.CreateResponse(HttpStatusCode.OK, new[] { "value1", "value2" });
|
||||
response.Headers.Add("Custom-Response-Test-Header1", "Test-Value1");
|
||||
response.Headers.Add("Custom-Response-Test-Header2", "Test-Value2");
|
||||
response.Headers.Add("Custom-Response-Test-Header3", "Test-Value3");
|
||||
return response;
|
||||
}
|
||||
|
||||
// GET api/values/5
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.3.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
|
@ -35,7 +35,7 @@
|
|||
<system.web>
|
||||
<compilation>
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
<add assembly="System.Web.Mvc, Version=5.3.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using Greet;
|
||||
using Grpc.Core;
|
||||
|
||||
namespace TestApplication.GrpcNetClient;
|
||||
|
||||
public class GreeterService : Greeter.GreeterBase
|
||||
{
|
||||
public override async Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
|
||||
{
|
||||
var headers = new Metadata
|
||||
{
|
||||
{ "Custom-Response-Test-Header1", "Test-Value1" },
|
||||
{ "Custom-Response-Test-Header2", "Test-Value2" },
|
||||
{ "Custom-Response-Test-Header3", "Test-Value3" }
|
||||
};
|
||||
|
||||
await context.WriteResponseHeadersAsync(headers);
|
||||
|
||||
return new HelloReply
|
||||
{
|
||||
Message = "Hello " + request.Name
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,43 +1,78 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
using Greet;
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using IntegrationTests.Helpers;
|
||||
#if !NETFRAMEWORK
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
#endif
|
||||
using TestApplication.GrpcNetClient;
|
||||
#if NETFRAMEWORK
|
||||
using Grpc.Net.Client.Web;
|
||||
#endif
|
||||
using TestApplication.Shared;
|
||||
|
||||
namespace TestApplication.GrpcNetClient;
|
||||
ConsoleHelper.WriteSplashScreen(args);
|
||||
|
||||
public static class Program
|
||||
var port = TcpPortProvider.GetOpenPort();
|
||||
|
||||
#if !NETFRAMEWORK
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.WebHost.ConfigureKestrel(serverOptions =>
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
serverOptions.ConfigureEndpointDefaults(listenOptions =>
|
||||
{
|
||||
ConsoleHelper.WriteSplashScreen(args);
|
||||
listenOptions.Protocols = HttpProtocols.Http2;
|
||||
});
|
||||
serverOptions.Listen(IPAddress.Loopback, port);
|
||||
});
|
||||
|
||||
const string uri = "http://dummyAdress";
|
||||
#if NETFRAMEWORK
|
||||
var channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
|
||||
{
|
||||
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
|
||||
});
|
||||
#else
|
||||
var channel = GrpcChannel.ForAddress(uri);
|
||||
// Add services to the container.
|
||||
builder.Services.AddGrpc();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.MapGrpcService<GreeterService>();
|
||||
|
||||
await app.StartAsync();
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
var greeterClient = new Greeter.GreeterClient(channel);
|
||||
await greeterClient.SayHelloAsync(new HelloRequest());
|
||||
}
|
||||
catch (RpcException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
var uri = $"http://localhost:{port}";
|
||||
#if NETFRAMEWORK
|
||||
var channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
|
||||
{
|
||||
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
|
||||
});
|
||||
#else
|
||||
var channel = GrpcChannel.ForAddress(uri);
|
||||
#endif
|
||||
|
||||
var headers = new Metadata
|
||||
{
|
||||
{ "Custom-Request-Test-Header1", "Test-Value1" },
|
||||
{ "Custom-Request-Test-Header2", "Test-Value2" },
|
||||
{ "Custom-Request-Test-Header3", "Test-Value3" }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var greeterClient = new Greeter.GreeterClient(channel);
|
||||
await greeterClient.SayHelloAsync(new HelloRequest { Name = "Test user" }, headers);
|
||||
}
|
||||
catch (RpcException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
#if !NETFRAMEWORK
|
||||
app.Lifetime.StopApplication();
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,9 @@ service Greeter {
|
|||
}
|
||||
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<!-- No warn is needed for autogenerated code by protobuf: CS8981 - The type name only contains lower-cased ascii characters. -->
|
||||
<NoWarn Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net7.0'">CS8981</NoWarn>
|
||||
<NoWarn Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net7.0'">CS8981;$(NoWarn)</NoWarn>
|
||||
<!-- Allow to downgrade Grpc.Tools on MacOS 11. -->
|
||||
<NoWarn Condition="'$(GITHUB_RUNNER_SYSTEM)' == 'macos-11'">NU1605;$(NoWarn)</NoWarn>
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" VersionOverride="3.22.5" />
|
||||
<PackageReference Include="Google.Protobuf" Condition="'$(TargetFramework)' == 'net462' or ( '$(LibraryVersion)' != '' and '$(LibraryVersion)' <= '2.54.0' )" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Condition="'$(TargetFramework)' != 'net462'" VersionOverride="$(LibraryVersion)" />
|
||||
<PackageReference Include="Grpc.Net.Client" VersionOverride="$(LibraryVersion)" />
|
||||
<PackageReference Include="Grpc.Net.Client.Web" Condition="'$(TargetFramework)' == 'net462'" VersionOverride="$(LibraryVersion)" />
|
||||
<PackageReference Include="Grpc.Tools" VersionOverride="2.44.0" />
|
||||
<PackageReference Include="Grpc.Tools" Condition="'$(TargetFramework)' == 'net462' or '$(GITHUB_RUNNER_SYSTEM)' == 'macos-11'" />
|
||||
<!-- Workaround! Microsoft.Extensions.Logging.Abstractions v.8.0.0 is minimal version supported by auto instrumentation.
|
||||
Grpc.Net.Client references older version. It prevents to load required version from Additional Dependencies store-->
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" VersionOverride="8.0.0" />
|
||||
|
@ -19,7 +23,12 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Proto\greet.proto" GrpcServices="Client" />
|
||||
<Protobuf Include="Proto\greet.proto" GrpcServices="Client,Server" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\IntegrationTests\Helpers\TcpPortProvider.cs">
|
||||
<Link>TcpPortProvider.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -16,8 +16,11 @@ public class Program
|
|||
var request = (HttpWebRequest)WebRequest.Create($"{address}/test");
|
||||
request.Method = "POST";
|
||||
request.ContentType = "text/plain";
|
||||
request.Headers.Add("Custom-Request-Test-Header1", "Test-Value1");
|
||||
request.Headers.Add("Custom-Request-Test-Header2", "Test-Value2");
|
||||
request.Headers.Add("Custom-Request-Test-Header3", "Test-Value3");
|
||||
|
||||
using (Stream requestStream = request.GetRequestStream())
|
||||
using (var requestStream = request.GetRequestStream())
|
||||
{
|
||||
var content = Encoding.UTF8.GetBytes("Ping");
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using System.Net;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
using System.Text;
|
||||
using TestApplication.Http.NetFramework.Helpers;
|
||||
|
||||
|
@ -12,13 +13,13 @@ public class TestServer : IDisposable
|
|||
private readonly HttpListener _listener;
|
||||
private readonly Thread _listenerThread;
|
||||
|
||||
public TestServer(string sufix)
|
||||
public TestServer(string suffix)
|
||||
{
|
||||
Port = TcpPortProvider.GetOpenPort();
|
||||
|
||||
_listener = new HttpListener();
|
||||
_listener.Start();
|
||||
var prefix = new UriBuilder("http", "localhost", Port, sufix).ToString();
|
||||
var prefix = new UriBuilder("http", "localhost", Port, suffix).ToString();
|
||||
_listener.Prefixes.Add(prefix);
|
||||
Console.WriteLine($"[LISTENER] Listening on '{prefix}'");
|
||||
|
||||
|
@ -53,6 +54,9 @@ public class TestServer : IDisposable
|
|||
|
||||
// NOTE: HttpStreamRequest doesn't support Transfer-Encoding: Chunked
|
||||
// (Setting content-length avoids that)
|
||||
ctx.Response.Headers.Add("Custom-Response-Test-Header1", "Test-Value1");
|
||||
ctx.Response.Headers.Add("Custom-Response-Test-Header2", "Test-Value2");
|
||||
ctx.Response.Headers.Add("Custom-Response-Test-Header3", "Test-Value3");
|
||||
ctx.Response.ContentType = "text/plain";
|
||||
var buffer = Encoding.UTF8.GetBytes("Pong");
|
||||
ctx.Response.ContentLength64 = buffer.LongLength;
|
||||
|
@ -79,7 +83,7 @@ public class TestServer : IDisposable
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// somethig unexpected happened
|
||||
// something unexpected happened
|
||||
// log instead of crashing the thread
|
||||
Console.WriteLine("[EXCEPTION]: {0}", ex.Message);
|
||||
Console.WriteLine(ex);
|
||||
|
|
|
@ -32,6 +32,9 @@ public class Program
|
|||
var address = addressFeature?.Addresses.First();
|
||||
var dnsAddress = address?.Replace("127.0.0.1", "localhost"); // needed to force DNS resolution to test metrics
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.DefaultRequestHeaders.Add("Custom-Request-Test-Header1", "Test-Value1");
|
||||
httpClient.DefaultRequestHeaders.Add("Custom-Request-Test-Header2", "Test-Value2");
|
||||
httpClient.DefaultRequestHeaders.Add("Custom-Request-Test-Header3", "Test-Value3");
|
||||
httpClient.GetAsync($"{dnsAddress}/test").Wait();
|
||||
httpClient.GetAsync($"{dnsAddress}/exception").Wait();
|
||||
#if NET8_0_OR_GREATER
|
||||
|
|
|
@ -40,6 +40,10 @@ public class Startup
|
|||
activity?.SetTag("test_tag", "test_value");
|
||||
}
|
||||
|
||||
context.Response.Headers.Append("Custom-Response-Test-Header1", "Test-Value1");
|
||||
context.Response.Headers.Append("Custom-Response-Test-Header2", "Test-Value2");
|
||||
context.Response.Headers.Append("Custom-Response-Test-Header3", "Test-Value3");
|
||||
|
||||
await context.Response.WriteAsync("Pong");
|
||||
}))
|
||||
.Map(
|
||||
|
|
Loading…
Reference in New Issue